@@ -25,6 +25,4 @@ | |||||
#include "modules/juce_gui_extra/juce_gui_extra.h" | #include "modules/juce_gui_extra/juce_gui_extra.h" | ||||
#include "modules/juce_tracktion_marketplace/juce_tracktion_marketplace.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 | #endif // JUCE_PLUGIN_MAIN_H_INCLUDED |
@@ -139,7 +139,7 @@ | |||||
@see VSTPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU, JUCE_PLUGINHOST_VST3 | @see VSTPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU, JUCE_PLUGINHOST_VST3 | ||||
*/ | */ | ||||
#define JUCE_PLUGINHOST_VST 0 | |||||
#define JUCE_PLUGINHOST_VST 1 | |||||
/** Config: JUCE_PLUGINHOST_VST3 | /** Config: JUCE_PLUGINHOST_VST3 | ||||
Enables the VST3 audio plugin hosting classes. This requires the Steinberg VST3 SDK to be | 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)); | #define JUCE_INCREMENT_DEST dest += (16 / sizeof (*dest)); | ||||
#if JUCE_USE_SSE_INTRINSICS | #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 | inline static bool isAligned (const void* p) noexcept | ||||
{ | { | ||||
return (((pointer_sized_int) p) & 15) == 0; | return (((pointer_sized_int) p) & 15) == 0; | ||||
@@ -113,7 +102,6 @@ namespace FloatVectorHelpers | |||||
#define JUCE_BEGIN_VEC_OP \ | #define JUCE_BEGIN_VEC_OP \ | ||||
typedef FloatVectorHelpers::ModeType<sizeof(*dest)>::Mode Mode; \ | typedef FloatVectorHelpers::ModeType<sizeof(*dest)>::Mode Mode; \ | ||||
if (FloatVectorHelpers::isSSE2Available()) \ | |||||
{ \ | { \ | ||||
const int numLongOps = num / Mode::numParallel; | const int numLongOps = num / Mode::numParallel; | ||||
@@ -372,11 +360,7 @@ namespace FloatVectorHelpers | |||||
{ | { | ||||
int numLongOps = num / Mode::numParallel; | int numLongOps = num / Mode::numParallel; | ||||
#if JUCE_USE_SSE_INTRINSICS | |||||
if (numLongOps > 1 && isSSE2Available()) | |||||
#else | |||||
if (numLongOps > 1) | if (numLongOps > 1) | ||||
#endif | |||||
{ | { | ||||
ParallelType val; | ParallelType val; | ||||
@@ -446,11 +430,7 @@ namespace FloatVectorHelpers | |||||
{ | { | ||||
int numLongOps = num / Mode::numParallel; | int numLongOps = num / Mode::numParallel; | ||||
#if JUCE_USE_SSE_INTRINSICS | |||||
if (numLongOps > 1 && isSSE2Available()) | |||||
#else | |||||
if (numLongOps > 1) | if (numLongOps > 1) | ||||
#endif | |||||
{ | { | ||||
ParallelType mn, mx; | ParallelType mn, mx; | ||||
@@ -1002,12 +982,19 @@ double JUCE_CALLTYPE FloatVectorOperations::findMaximum (const double* src, int | |||||
void JUCE_CALLTYPE FloatVectorOperations::enableFlushToZeroMode (bool shouldEnable) noexcept | void JUCE_CALLTYPE FloatVectorOperations::enableFlushToZeroMode (bool shouldEnable) noexcept | ||||
{ | { | ||||
#if JUCE_USE_SSE_INTRINSICS | #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 | #endif | ||||
ignoreUnused (shouldEnable); | 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 | #if JUCE_UNIT_TESTS | ||||
@@ -198,6 +198,12 @@ public: | |||||
Effectively, this is a wrapper around a call to _MM_SET_FLUSH_ZERO_MODE | Effectively, this is a wrapper around a call to _MM_SET_FLUSH_ZERO_MODE | ||||
*/ | */ | ||||
static void JUCE_CALLTYPE enableFlushToZeroMode (bool shouldEnable) noexcept; | 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 | else | ||||
{ | { | ||||
for (int i = 0; i < numOut; ++i) | 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 | 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. | 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 | 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 | multiple channels, make sure each one uses its own LagrangeInterpolator | ||||
object. | object. | ||||
@see CatmullRomInterpolator | |||||
*/ | */ | ||||
class JUCE_API LagrangeInterpolator | class JUCE_API LagrangeInterpolator | ||||
{ | { | ||||
public: | public: | ||||
LagrangeInterpolator(); | |||||
~LagrangeInterpolator(); | |||||
LagrangeInterpolator() noexcept; | |||||
~LagrangeInterpolator() noexcept; | |||||
/** Resets the state of the interpolator. | /** Resets the state of the interpolator. | ||||
Call this when there's a break in the continuity of the input data stream. | 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) | 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. */ | /** Reset to a new sample rate and ramp length. */ | ||||
void reset (double sampleRate, double rampLengthInSeconds) noexcept | void reset (double sampleRate, double rampLengthInSeconds) noexcept | ||||
{ | { | ||||
@@ -59,7 +59,7 @@ public: | |||||
countdown = 0; | countdown = 0; | ||||
} | } | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Set a new target value. */ | /** Set a new target value. */ | ||||
void setValue (FloatType newValue) noexcept | void setValue (FloatType newValue) noexcept | ||||
{ | { | ||||
@@ -75,7 +75,7 @@ public: | |||||
} | } | ||||
} | } | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Compute the next value. */ | /** Compute the next value. */ | ||||
FloatType getNextValue() noexcept | FloatType getNextValue() noexcept | ||||
{ | { | ||||
@@ -88,7 +88,7 @@ public: | |||||
} | } | ||||
private: | private: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
FloatType currentValue, target, step; | FloatType currentValue, target, step; | ||||
int countdown, stepsToTarget; | int countdown, stepsToTarget; | ||||
}; | }; | ||||
@@ -58,9 +58,7 @@ | |||||
#endif | #endif | ||||
#if (JUCE_MAC || JUCE_IOS) && JUCE_USE_VDSP_FRAMEWORK | #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> | #include <Accelerate/Accelerate.h> | ||||
#undef Point | |||||
#else | #else | ||||
#undef JUCE_USE_VDSP_FRAMEWORK | #undef JUCE_USE_VDSP_FRAMEWORK | ||||
#endif | #endif | ||||
@@ -81,6 +79,7 @@ namespace juce | |||||
#include "effects/juce_IIRFilter.cpp" | #include "effects/juce_IIRFilter.cpp" | ||||
#include "effects/juce_IIRFilterOld.cpp" | #include "effects/juce_IIRFilterOld.cpp" | ||||
#include "effects/juce_LagrangeInterpolator.cpp" | #include "effects/juce_LagrangeInterpolator.cpp" | ||||
#include "effects/juce_CatmullRomInterpolator.cpp" | |||||
#include "effects/juce_FFT.cpp" | #include "effects/juce_FFT.cpp" | ||||
#include "midi/juce_MidiBuffer.cpp" | #include "midi/juce_MidiBuffer.cpp" | ||||
#include "midi/juce_MidiFile.cpp" | #include "midi/juce_MidiFile.cpp" | ||||
@@ -27,7 +27,7 @@ | |||||
#include "../juce_core/juce_core.h" | #include "../juce_core/juce_core.h" | ||||
//============================================================================= | |||||
//============================================================================== | |||||
namespace juce | namespace juce | ||||
{ | { | ||||
@@ -41,6 +41,7 @@ namespace juce | |||||
#include "effects/juce_IIRFilter.h" | #include "effects/juce_IIRFilter.h" | ||||
#include "effects/juce_IIRFilterOld.h" | #include "effects/juce_IIRFilterOld.h" | ||||
#include "effects/juce_LagrangeInterpolator.h" | #include "effects/juce_LagrangeInterpolator.h" | ||||
#include "effects/juce_CatmullRomInterpolator.h" | |||||
#include "effects/juce_FFT.h" | #include "effects/juce_FFT.h" | ||||
#include "effects/juce_LinearSmoothedValue.h" | #include "effects/juce_LinearSmoothedValue.h" | ||||
#include "effects/juce_Reverb.h" | #include "effects/juce_Reverb.h" | ||||
@@ -371,7 +371,7 @@ int MidiMessage::getNoteNumber() const noexcept | |||||
void MidiMessage::setNoteNumber (const int newNoteNumber) noexcept | void MidiMessage::setNoteNumber (const int newNoteNumber) noexcept | ||||
{ | { | ||||
if (isNoteOnOrOff()) | |||||
if (isNoteOnOrOff() || isAftertouch()) | |||||
getData()[1] = (uint8) (newNoteNumber & 127); | 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, | void MidiMessageSequence::addSequence (const MidiMessageSequence& other, | ||||
double timeAdjustment, | double timeAdjustment, | ||||
double firstAllowableTime, | double firstAllowableTime, | ||||
double endOfAllowableDestTimes) | double endOfAllowableDestTimes) | ||||
{ | { | ||||
firstAllowableTime -= timeAdjustment; | |||||
endOfAllowableDestTimes -= timeAdjustment; | |||||
for (int i = 0; i < other.list.size(); ++i) | for (int i = 0; i < other.list.size(); ++i) | ||||
{ | { | ||||
const MidiMessage& m = other.list.getUnchecked(i)->message; | const MidiMessage& m = other.list.getUnchecked(i)->message; | ||||
const double t = m.getTimeStamp(); | |||||
const double t = m.getTimeStamp() + timeAdjustment; | |||||
if (t >= firstAllowableTime && t < endOfAllowableDestTimes) | if (t >= firstAllowableTime && t < endOfAllowableDestTimes) | ||||
{ | { | ||||
MidiEventHolder* const newOne = new MidiEventHolder (m); | MidiEventHolder* const newOne = new MidiEventHolder (m); | ||||
newOne->message.setTimeStamp (timeAdjustment + t); | |||||
newOne->message.setTimeStamp (t); | |||||
list.add (newOne); | list.add (newOne); | ||||
} | } | ||||
@@ -160,7 +160,6 @@ public: | |||||
void deleteEvent (int index, bool deleteMatchingNoteUp); | void deleteEvent (int index, bool deleteMatchingNoteUp); | ||||
/** Merges another sequence into this one. | /** Merges another sequence into this one. | ||||
Remember to call updateMatchedPairs() after using this method. | Remember to call updateMatchedPairs() after using this method. | ||||
@param other the sequence to add from | @param other the sequence to add from | ||||
@@ -178,6 +177,16 @@ public: | |||||
double firstAllowableDestTime, | double firstAllowableDestTime, | ||||
double endOfAllowableDestTimes); | 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. | /** Makes sure all the note-on and note-off pairs are up-to-date. | ||||
@@ -331,7 +331,7 @@ public: | |||||
} | } | ||||
private: | private: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
void expectContainsRPN (const MidiBuffer& midiBuffer, | void expectContainsRPN (const MidiBuffer& midiBuffer, | ||||
int channel, | int channel, | ||||
int parameterNumber, | int parameterNumber, | ||||
@@ -343,7 +343,7 @@ private: | |||||
expectContainsRPN (midiBuffer, expected); | expectContainsRPN (midiBuffer, expected); | ||||
} | } | ||||
//========================================================================== | |||||
//============================================================================== | |||||
void expectContainsRPN (const MidiBuffer& midiBuffer, MidiRPNMessage expected) | void expectContainsRPN (const MidiBuffer& midiBuffer, MidiRPNMessage expected) | ||||
{ | { | ||||
MidiBuffer::Iterator iter (midiBuffer); | MidiBuffer::Iterator iter (midiBuffer); | ||||
@@ -26,7 +26,7 @@ | |||||
#define JUCE_MIDIRPNDETECTOR_H_INCLUDED | #define JUCE_MIDIRPNDETECTOR_H_INCLUDED | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Represents a MIDI RPN (registered parameter number) or NRPN (non-registered | /** Represents a MIDI RPN (registered parameter number) or NRPN (non-registered | ||||
parameter number) message. | parameter number) message. | ||||
*/ | */ | ||||
@@ -77,7 +77,7 @@ public: | |||||
*/ | */ | ||||
void reset() noexcept; | void reset() noexcept; | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Takes the next in a stream of incoming MIDI CC messages and returns true | /** 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. | if it forms the last of a sequence that makes an RPN or NPRN. | ||||
@@ -91,7 +91,7 @@ public: | |||||
MidiRPNMessage& result) noexcept; | MidiRPNMessage& result) noexcept; | ||||
private: | private: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
struct ChannelState | struct ChannelState | ||||
{ | { | ||||
ChannelState() noexcept; | ChannelState() noexcept; | ||||
@@ -104,7 +104,7 @@ private: | |||||
bool isNRPN; | bool isNRPN; | ||||
}; | }; | ||||
//========================================================================== | |||||
//============================================================================== | |||||
ChannelState states[16]; | ChannelState states[16]; | ||||
JUCE_LEAK_DETECTOR (MidiRPNDetector) | JUCE_LEAK_DETECTOR (MidiRPNDetector) | ||||
@@ -120,11 +120,11 @@ private: | |||||
class JUCE_API MidiRPNGenerator | class JUCE_API MidiRPNGenerator | ||||
{ | { | ||||
public: | public: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Generates a MIDI sequence representing the given RPN or NRPN message. */ | /** Generates a MIDI sequence representing the given RPN or NRPN message. */ | ||||
static MidiBuffer generate (MidiRPNMessage message); | static MidiBuffer generate (MidiRPNMessage message); | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Generates a MIDI sequence representing an RPN or NRPN message with the | /** Generates a MIDI sequence representing an RPN or NRPN message with the | ||||
given parameters. | given parameters. | ||||
@@ -135,14 +135,6 @@ void MPEInstrument::removeListener (Listener* const listenerToRemove) noexcept | |||||
listeners.remove (listenerToRemove); | listeners.remove (listenerToRemove); | ||||
} | } | ||||
MPEInstrument::Listener::Listener() | |||||
{ | |||||
} | |||||
MPEInstrument::Listener::~Listener() | |||||
{ | |||||
} | |||||
//============================================================================== | //============================================================================== | ||||
void MPEInstrument::processNextMidiEvent (const MidiMessage& message) | void MPEInstrument::processNextMidiEvent (const MidiMessage& message) | ||||
{ | { | ||||
@@ -1976,7 +1968,7 @@ public: | |||||
} | } | ||||
private: | private: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/* This mock class is used for unit testing whether the methods of | /* This mock class is used for unit testing whether the methods of | ||||
MPEInstrument are called correctly. | MPEInstrument are called correctly. | ||||
*/ | */ | ||||
@@ -2074,7 +2066,7 @@ private: | |||||
ScopedPointer<MPENote> lastNoteFinished; | ScopedPointer<MPENote> lastNoteFinished; | ||||
private: | private: | ||||
//====================================================================== | |||||
//============================================================================== | |||||
void noteAdded (MPENote) override { noteAddedCallCounter++; } | void noteAdded (MPENote) override { noteAddedCallCounter++; } | ||||
void notePressureChanged (MPENote) override { notePressureChangedCallCounter++; } | void notePressureChanged (MPENote) override { notePressureChangedCallCounter++; } | ||||
@@ -2089,7 +2081,7 @@ private: | |||||
} | } | ||||
}; | }; | ||||
//========================================================================== | |||||
//============================================================================== | |||||
template <int initial7BitPressure, int initial14BitPitchbend, int initial7BitTimbre> | template <int initial7BitPressure, int initial14BitPitchbend, int initial7BitTimbre> | ||||
class CustomInitialValuesTest : public MPEInstrument | class CustomInitialValuesTest : public MPEInstrument | ||||
{ | { | ||||
@@ -2109,7 +2101,7 @@ private: | |||||
} | } | ||||
}; | }; | ||||
//========================================================================== | |||||
//============================================================================== | |||||
void expectNote (MPENote noteToTest, | void expectNote (MPENote noteToTest, | ||||
int noteOnVelocity7Bit, | int noteOnVelocity7Bit, | ||||
int pressure7Bit, | int pressure7Bit, | ||||
@@ -2141,7 +2133,7 @@ private: | |||||
expect (std::fabs (expected - actual) < maxAbsoluteError); | expect (std::fabs (expected - actual) < maxAbsoluteError); | ||||
} | } | ||||
//========================================================================== | |||||
//============================================================================== | |||||
MPEZoneLayout testLayout; | MPEZoneLayout testLayout; | ||||
}; | }; | ||||
@@ -68,7 +68,7 @@ public: | |||||
/** Destructor. */ | /** Destructor. */ | ||||
virtual ~MPEInstrument(); | virtual ~MPEInstrument(); | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Returns the current zone layout of the instrument. | /** Returns the current zone layout of the instrument. | ||||
This happens by value, to enforce thread-safety and class invariants. | This happens by value, to enforce thread-safety and class invariants. | ||||
@@ -98,7 +98,7 @@ public: | |||||
*/ | */ | ||||
bool isMasterChannel (int midiChannel) const noexcept; | bool isMasterChannel (int midiChannel) const noexcept; | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** The MPE note tracking mode. In case there is more than one note playing | /** 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 | simultaneously on the same MIDI channel, this determines which of these | ||||
notes will be modulated by an incoming MPE message on that channel | 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. */ | /** Set the MPE tracking mode for the timbre dimension. */ | ||||
void setTimbreTrackingMode (TrackingMode modeToUse); | void setTimbreTrackingMode (TrackingMode modeToUse); | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Process a MIDI message and trigger the appropriate method calls | /** Process a MIDI message and trigger the appropriate method calls | ||||
(noteOn, noteOff etc.) | (noteOn, noteOff etc.) | ||||
@@ -132,7 +132,7 @@ public: | |||||
*/ | */ | ||||
virtual void processNextMidiEvent (const MidiMessage& message); | virtual void processNextMidiEvent (const MidiMessage& message); | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Request a note-on on the given channel, with the given initial note | /** Request a note-on on the given channel, with the given initial note | ||||
number and velocity. | number and velocity. | ||||
If the message arrives on a valid note channel, this will create a | If the message arrives on a valid note channel, this will create a | ||||
@@ -187,7 +187,7 @@ public: | |||||
*/ | */ | ||||
void releaseAllNotes(); | void releaseAllNotes(); | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Returns the number of MPE notes currently played by the | /** Returns the number of MPE notes currently played by the | ||||
instrument. | instrument. | ||||
*/ | */ | ||||
@@ -221,7 +221,7 @@ public: | |||||
*/ | */ | ||||
MPENote getMostRecentNoteOtherThan (MPENote otherThanThisNote) const noexcept; | MPENote getMostRecentNoteOtherThan (MPENote otherThanThisNote) const noexcept; | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Derive from this class to be informed about any changes in the expressive | /** Derive from this class to be informed about any changes in the expressive | ||||
MIDI notes played by this instrument. | MIDI notes played by this instrument. | ||||
@@ -230,14 +230,11 @@ public: | |||||
Therefore you should never do heavy work such as graphics rendering etc. | Therefore you should never do heavy work such as graphics rendering etc. | ||||
inside those callbacks. | inside those callbacks. | ||||
*/ | */ | ||||
class Listener | |||||
class JUCE_API Listener | |||||
{ | { | ||||
public: | public: | ||||
/** Constructor. */ | |||||
Listener(); | |||||
/** Destructor. */ | /** Destructor. */ | ||||
virtual ~Listener(); | |||||
virtual ~Listener() {} | |||||
/** Implement this callback to be informed whenever a new expressive | /** Implement this callback to be informed whenever a new expressive | ||||
MIDI note is triggered. | MIDI note is triggered. | ||||
@@ -278,14 +275,14 @@ public: | |||||
virtual void noteReleased (MPENote finishedNote) = 0; | virtual void noteReleased (MPENote finishedNote) = 0; | ||||
}; | }; | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Adds a listener. */ | /** Adds a listener. */ | ||||
void addListener (Listener* const listenerToAdd) noexcept; | |||||
void addListener (Listener* listenerToAdd) noexcept; | |||||
/** Removes a listener. */ | /** Removes a listener. */ | ||||
void removeListener (Listener* const listenerToRemove) noexcept; | |||||
void removeListener (Listener* listenerToRemove) noexcept; | |||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Puts the instrument into legacy mode. | /** Puts the instrument into legacy mode. | ||||
As a side effect, this will discard all currently playing notes, | As a side effect, this will discard all currently playing notes, | ||||
and call noteReleased for all of them. | and call noteReleased for all of them. | ||||
@@ -324,7 +321,7 @@ public: | |||||
void setLegacyModePitchbendRange (int pitchbendRange); | void setLegacyModePitchbendRange (int pitchbendRange); | ||||
protected: | protected: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** This method defines what initial pitchbend value should be used for newly | /** This method defines what initial pitchbend value should be used for newly | ||||
triggered notes. The default is to use the last pitchbend value | triggered notes. The default is to use the last pitchbend value | ||||
that has been received on the same MIDI channel (or no pitchbend | that has been received on the same MIDI channel (or no pitchbend | ||||
@@ -354,7 +351,7 @@ protected: | |||||
MPEValue midiNoteOnVelocity) const; | MPEValue midiNoteOnVelocity) const; | ||||
private: | private: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
CriticalSection lock; | CriticalSection lock; | ||||
Array<MPENote> notes; | Array<MPENote> notes; | ||||
MPEZoneLayout zoneLayout; | MPEZoneLayout zoneLayout; | ||||
@@ -162,7 +162,7 @@ public: | |||||
} | } | ||||
private: | private: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
void testMidiBuffer (MidiBuffer& buffer, const uint8* expectedBytes, int expectedBytesSize) | void testMidiBuffer (MidiBuffer& buffer, const uint8* expectedBytes, int expectedBytesSize) | ||||
{ | { | ||||
uint8 actualBytes[128] = { 0 }; | uint8 actualBytes[128] = { 0 }; | ||||
@@ -171,7 +171,7 @@ private: | |||||
expectEquals (std::memcmp (actualBytes, expectedBytes, (std::size_t) expectedBytesSize), 0); | expectEquals (std::memcmp (actualBytes, expectedBytes, (std::size_t) expectedBytesSize), 0); | ||||
} | } | ||||
//========================================================================== | |||||
//============================================================================== | |||||
void extractRawBinaryData (const MidiBuffer& midiBuffer, const uint8* bufferToCopyTo, std::size_t maxBytes) | void extractRawBinaryData (const MidiBuffer& midiBuffer, const uint8* bufferToCopyTo, std::size_t maxBytes) | ||||
{ | { | ||||
std::size_t pos = 0; | std::size_t pos = 0; | ||||
@@ -103,7 +103,7 @@ class MPENoteTests : public UnitTest | |||||
public: | public: | ||||
MPENoteTests() : UnitTest ("MPENote class") {} | MPENoteTests() : UnitTest ("MPENote class") {} | ||||
//========================================================================== | |||||
//============================================================================== | |||||
void runTest() override | void runTest() override | ||||
{ | { | ||||
beginTest ("getFrequencyInHertz"); | beginTest ("getFrequencyInHertz"); | ||||
@@ -116,7 +116,7 @@ public: | |||||
} | } | ||||
private: | private: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
void expectEqualsWithinOneCent (double frequencyInHertzActual, | void expectEqualsWithinOneCent (double frequencyInHertzActual, | ||||
double frequencyInHertzExpected) | double frequencyInHertzExpected) | ||||
{ | { | ||||
@@ -39,7 +39,7 @@ | |||||
*/ | */ | ||||
struct JUCE_API MPENote | struct JUCE_API MPENote | ||||
{ | { | ||||
//========================================================================== | |||||
//============================================================================== | |||||
enum KeyState | enum KeyState | ||||
{ | { | ||||
off = 0, | off = 0, | ||||
@@ -48,7 +48,7 @@ struct JUCE_API MPENote | |||||
keyDownAndSustained = 3 | keyDownAndSustained = 3 | ||||
}; | }; | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Constructor. | /** Constructor. | ||||
@param midiChannel The MIDI channel of the note, between 2 and 16. | @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. */ | /** Checks whether the MPE note is valid. */ | ||||
bool isValid() const noexcept; | bool isValid() const noexcept; | ||||
//========================================================================== | |||||
//============================================================================== | |||||
// Invariants that define the note. | // Invariants that define the note. | ||||
/** A unique ID. Useful to distinguish the note from other simultaneously | /** A unique ID. Useful to distinguish the note from other simultaneously | ||||
@@ -107,7 +107,7 @@ struct JUCE_API MPENote | |||||
*/ | */ | ||||
uint8 initialNote; | uint8 initialNote; | ||||
//========================================================================== | |||||
//============================================================================== | |||||
// The five dimensions of continuous expressive control | // The five dimensions of continuous expressive control | ||||
/** The velocity ("strike") of the note-on. | /** The velocity ("strike") of the note-on. | ||||
@@ -146,7 +146,7 @@ struct JUCE_API MPENote | |||||
*/ | */ | ||||
MPEValue noteOffVelocity; | MPEValue noteOffVelocity; | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Current effective pitchbend of the note in units of semitones, relative | /** Current effective pitchbend of the note in units of semitones, relative | ||||
to initialNote. You should use this to compute the actual effective pitch | 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 | of the note. This value is computed and set by an MPEInstrument to the | ||||
@@ -163,7 +163,7 @@ struct JUCE_API MPENote | |||||
*/ | */ | ||||
KeyState keyState; | KeyState keyState; | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Returns the current frequency of the note in Hertz. This is the a sum of | /** Returns the current frequency of the note in Hertz. This is the a sum of | ||||
the initialNote and the totalPitchbendInSemitones, converted to Hertz. | the initialNote and the totalPitchbendInSemitones, converted to Hertz. | ||||
*/ | */ | ||||
@@ -55,7 +55,7 @@ | |||||
class JUCE_API MPESynthesiser : public MPESynthesiserBase | class JUCE_API MPESynthesiser : public MPESynthesiserBase | ||||
{ | { | ||||
public: | public: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Constructor. | /** Constructor. | ||||
You'll need to add some voices before it'll make any sound. | You'll need to add some voices before it'll make any sound. | ||||
@@ -75,7 +75,7 @@ public: | |||||
/** Destructor. */ | /** Destructor. */ | ||||
~MPESynthesiser(); | ~MPESynthesiser(); | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Deletes all voices. */ | /** Deletes all voices. */ | ||||
void clearVoices(); | void clearVoices(); | ||||
@@ -116,7 +116,7 @@ public: | |||||
*/ | */ | ||||
virtual void turnOffAllVoices (bool allowTailOff); | virtual void turnOffAllVoices (bool allowTailOff); | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** If set to true, then the synth will try to take over an existing voice if | /** 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. | it runs out and needs to play another note. | ||||
@@ -128,7 +128,7 @@ public: | |||||
/** Returns true if note-stealing is enabled. */ | /** Returns true if note-stealing is enabled. */ | ||||
bool isVoiceStealingEnabled() const noexcept { return shouldStealVoices; } | bool isVoiceStealingEnabled() const noexcept { return shouldStealVoices; } | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Tells the synthesiser what the sample rate is for the audio it's being used to render. | /** 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 | This overrides the implementation in MPESynthesiserBase, to additionally | ||||
@@ -137,7 +137,7 @@ public: | |||||
*/ | */ | ||||
void setCurrentPlaybackSampleRate (double newRate) override; | void setCurrentPlaybackSampleRate (double newRate) override; | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Handle incoming MIDI events. | /** Handle incoming MIDI events. | ||||
This method will be called automatically according to the MIDI data passed | This method will be called automatically according to the MIDI data passed | ||||
@@ -238,7 +238,7 @@ protected: | |||||
*/ | */ | ||||
virtual void noteKeyStateChanged (MPENote changedNote) override; | virtual void noteKeyStateChanged (MPENote changedNote) override; | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** This will simply call renderNextBlock for each currently active | /** This will simply call renderNextBlock for each currently active | ||||
voice and fill the buffer with the sum. | voice and fill the buffer with the sum. | ||||
Override this method if you need to do more work to render your audio. | Override this method if you need to do more work to render your audio. | ||||
@@ -255,7 +255,7 @@ protected: | |||||
int startSample, | int startSample, | ||||
int numSamples) override; | int numSamples) override; | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Searches through the voices to find one that's not currently playing, and | /** Searches through the voices to find one that's not currently playing, and | ||||
which can play the given MPE note. | which can play the given MPE note. | ||||
@@ -298,11 +298,11 @@ protected: | |||||
*/ | */ | ||||
void stopVoice (MPESynthesiserVoice* voice, MPENote noteToStop, bool allowTailOff); | void stopVoice (MPESynthesiserVoice* voice, MPENote noteToStop, bool allowTailOff); | ||||
//========================================================================== | |||||
//============================================================================== | |||||
OwnedArray<MPESynthesiserVoice> voices; | OwnedArray<MPESynthesiserVoice> voices; | ||||
private: | private: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
bool shouldStealVoices; | bool shouldStealVoices; | ||||
CriticalSection voicesLock; | CriticalSection voicesLock; | ||||
@@ -47,7 +47,7 @@ | |||||
struct JUCE_API MPESynthesiserBase : public MPEInstrument::Listener | struct JUCE_API MPESynthesiserBase : public MPEInstrument::Listener | ||||
{ | { | ||||
public: | public: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Constructor. */ | /** Constructor. */ | ||||
MPESynthesiserBase(); | MPESynthesiserBase(); | ||||
@@ -61,7 +61,7 @@ public: | |||||
*/ | */ | ||||
MPESynthesiserBase (MPEInstrument* instrument); | MPESynthesiserBase (MPEInstrument* instrument); | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Returns the synthesiser's internal MPE zone layout. | /** Returns the synthesiser's internal MPE zone layout. | ||||
This happens by value, to enforce thread-safety and class invariants. | This happens by value, to enforce thread-safety and class invariants. | ||||
*/ | */ | ||||
@@ -73,7 +73,7 @@ public: | |||||
*/ | */ | ||||
void setZoneLayout (MPEZoneLayout newLayout); | void setZoneLayout (MPEZoneLayout newLayout); | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Tells the synthesiser what the sample rate is for the audio it's being | /** Tells the synthesiser what the sample rate is for the audio it's being | ||||
used to render. | used to render. | ||||
*/ | */ | ||||
@@ -84,7 +84,7 @@ public: | |||||
*/ | */ | ||||
double getSampleRate() const noexcept { return sampleRate; } | double getSampleRate() const noexcept { return sampleRate; } | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Creates the next block of audio output. | /** Creates the next block of audio output. | ||||
Call this to make sound. This will chop up the AudioBuffer into subBlock | Call this to make sound. This will chop up the AudioBuffer into subBlock | ||||
@@ -99,7 +99,7 @@ public: | |||||
int startSample, | int startSample, | ||||
int numSamples); | int numSamples); | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Handle incoming MIDI events (called from renderNextBlock). | /** Handle incoming MIDI events (called from renderNextBlock). | ||||
The default implementation provided here simply forwards everything | The default implementation provided here simply forwards everything | ||||
@@ -113,7 +113,7 @@ public: | |||||
*/ | */ | ||||
virtual void handleMidiEvent (const MidiMessage&); | virtual void handleMidiEvent (const MidiMessage&); | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Sets a minimum limit on the size to which audio sub-blocks will be divided when rendering. | /** 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 | When rendering, the audio blocks that are passed into renderNextBlock() will be split up | ||||
@@ -130,7 +130,7 @@ public: | |||||
*/ | */ | ||||
void setMinimumRenderingSubdivisionSize (int numSamples) noexcept; | void setMinimumRenderingSubdivisionSize (int numSamples) noexcept; | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Puts the synthesiser into legacy mode. | /** Puts the synthesiser into legacy mode. | ||||
@param pitchbendRange The note pitchbend range in semitones to use when in 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); | void setLegacyModePitchbendRange (int pitchbendRange); | ||||
protected: | protected: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Implement this method to render your audio inside. | /** Implement this method to render your audio inside. | ||||
@see renderNextBlock | @see renderNextBlock | ||||
*/ | */ | ||||
@@ -176,14 +176,14 @@ protected: | |||||
int /*numSamples*/) {} | int /*numSamples*/) {} | ||||
protected: | protected: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** @internal */ | /** @internal */ | ||||
ScopedPointer<MPEInstrument> instrument; | ScopedPointer<MPEInstrument> instrument; | ||||
/** @internal */ | /** @internal */ | ||||
CriticalSection renderAudioLock; | CriticalSection renderAudioLock; | ||||
private: | private: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
double sampleRate; | double sampleRate; | ||||
int minimumSubBlockSize; | int minimumSubBlockSize; | ||||
@@ -37,7 +37,7 @@ | |||||
class JUCE_API MPESynthesiserVoice | class JUCE_API MPESynthesiserVoice | ||||
{ | { | ||||
public: | public: | ||||
//======================================================================== | |||||
//============================================================================== | |||||
/** Constructor. */ | /** Constructor. */ | ||||
MPESynthesiserVoice(); | MPESynthesiserVoice(); | ||||
@@ -160,7 +160,7 @@ public: | |||||
bool wasStartedBefore (const MPESynthesiserVoice& other) const noexcept; | bool wasStartedBefore (const MPESynthesiserVoice& other) const noexcept; | ||||
protected: | protected: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Resets the state of this voice after a sound has finished playing. | /** 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 | The subclass must call this when it finishes playing a note and becomes available | ||||
@@ -175,12 +175,12 @@ protected: | |||||
*/ | */ | ||||
void clearCurrentNote() noexcept; | void clearCurrentNote() noexcept; | ||||
//========================================================================== | |||||
//============================================================================== | |||||
double currentSampleRate; | double currentSampleRate; | ||||
MPENote currentlyPlayingNote; | MPENote currentlyPlayingNote; | ||||
private: | private: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
friend class MPESynthesiser; | friend class MPESynthesiser; | ||||
uint32 noteStartTime; | uint32 noteStartTime; | ||||
@@ -144,7 +144,7 @@ public: | |||||
} | } | ||||
private: | private: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
void expectValuesConsistent (MPEValue value, | void expectValuesConsistent (MPEValue value, | ||||
int expectedValueAs7BitInt, | int expectedValueAs7BitInt, | ||||
int expectedValueAs14BitInt, | int expectedValueAs14BitInt, | ||||
@@ -157,7 +157,7 @@ private: | |||||
expectFloatWithinRelativeError (value.asUnsignedFloat(), expectedValueAsUnsignedFloat, 0.0001f); | expectFloatWithinRelativeError (value.asUnsignedFloat(), expectedValueAsUnsignedFloat, 0.0001f); | ||||
} | } | ||||
//========================================================================== | |||||
//============================================================================== | |||||
void expectFloatWithinRelativeError (float actualValue, float expectedValue, float maxRelativeError) | void expectFloatWithinRelativeError (float actualValue, float expectedValue, float maxRelativeError) | ||||
{ | { | ||||
const float maxAbsoluteError = jmax (1.0f, std::fabs (expectedValue)) * maxRelativeError; | const float maxAbsoluteError = jmax (1.0f, std::fabs (expectedValue)) * maxRelativeError; | ||||
@@ -37,7 +37,7 @@ | |||||
class JUCE_API MPEValue | class JUCE_API MPEValue | ||||
{ | { | ||||
public: | public: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Default constructor. Constructs an MPEValue corresponding | /** Default constructor. Constructs an MPEValue corresponding | ||||
to the centre value. | to the centre value. | ||||
*/ | */ | ||||
@@ -87,7 +87,7 @@ public: | |||||
bool operator!= (const MPEValue& other) const noexcept; | bool operator!= (const MPEValue& other) const noexcept; | ||||
private: | private: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
MPEValue (int normalisedValue); | MPEValue (int normalisedValue); | ||||
int normalisedValue; | int normalisedValue; | ||||
}; | }; | ||||
@@ -144,7 +144,7 @@ bool MPEZone::truncateToFit (MPEZone other) noexcept | |||||
return true; | return true; | ||||
} | } | ||||
//========================================================================== | |||||
//============================================================================== | |||||
bool MPEZone::operator== (const MPEZone& other) const noexcept | bool MPEZone::operator== (const MPEZone& other) const noexcept | ||||
{ | { | ||||
return masterChannel == other.masterChannel | return masterChannel == other.masterChannel | ||||
@@ -284,7 +284,7 @@ public: | |||||
} | } | ||||
private: | private: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
void testOverlapsWith (int masterChannelFirst, int numNoteChannelsFirst, | void testOverlapsWith (int masterChannelFirst, int numNoteChannelsFirst, | ||||
int masterChannelSecond, int numNoteChannelsSecond, | int masterChannelSecond, int numNoteChannelsSecond, | ||||
bool expectedRetVal) | bool expectedRetVal) | ||||
@@ -296,7 +296,7 @@ private: | |||||
expect (second.overlapsWith (first) == expectedRetVal); | expect (second.overlapsWith (first) == expectedRetVal); | ||||
} | } | ||||
//========================================================================== | |||||
//============================================================================== | |||||
void testTruncateToFit (int masterChannelFirst, int numNoteChannelsFirst, | void testTruncateToFit (int masterChannelFirst, int numNoteChannelsFirst, | ||||
int masterChannelSecond, int numNoteChannelsSecond, | int masterChannelSecond, int numNoteChannelsSecond, | ||||
bool expectedRetVal, | bool expectedRetVal, | ||||
@@ -127,7 +127,7 @@ struct JUCE_API MPEZone | |||||
bool operator!= (const MPEZone& other) const noexcept; | bool operator!= (const MPEZone& other) const noexcept; | ||||
private: | private: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
int masterChannel; | int masterChannel; | ||||
int numNoteChannels; | int numNoteChannels; | ||||
int perNotePitchbendRange; | int perNotePitchbendRange; | ||||
@@ -197,14 +197,6 @@ void MPEZoneLayout::removeListener (Listener* const listenerToRemove) noexcept | |||||
listeners.remove (listenerToRemove); | listeners.remove (listenerToRemove); | ||||
} | } | ||||
MPEZoneLayout::Listener::Listener() | |||||
{ | |||||
} | |||||
MPEZoneLayout::Listener::~Listener() | |||||
{ | |||||
} | |||||
//============================================================================== | //============================================================================== | ||||
//============================================================================== | //============================================================================== | ||||
#if JUCE_UNIT_TESTS | #if JUCE_UNIT_TESTS | ||||
@@ -125,18 +125,15 @@ public: | |||||
*/ | */ | ||||
MPEZone* getZoneByNoteChannel (int midiChannel) const noexcept; | MPEZone* getZoneByNoteChannel (int midiChannel) const noexcept; | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Listener class. Derive from this class to allow your class to be | /** Listener class. Derive from this class to allow your class to be | ||||
notified about changes to the zone layout. | notified about changes to the zone layout. | ||||
*/ | */ | ||||
class Listener | class Listener | ||||
{ | { | ||||
public: | public: | ||||
/** Constructor. */ | |||||
Listener(); | |||||
/** Destructor. */ | /** Destructor. */ | ||||
virtual ~Listener(); | |||||
virtual ~Listener() {} | |||||
/** Implement this callback to be notified about any changes to this | /** Implement this callback to be notified about any changes to this | ||||
MPEZoneLayout. Will be called whenever a zone is added, zones are | MPEZoneLayout. Will be called whenever a zone is added, zones are | ||||
@@ -145,7 +142,7 @@ public: | |||||
virtual void zoneLayoutChanged (const MPEZoneLayout& layout) = 0; | virtual void zoneLayoutChanged (const MPEZoneLayout& layout) = 0; | ||||
}; | }; | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** Adds a listener. */ | /** Adds a listener. */ | ||||
void addListener (Listener* const listenerToAdd) noexcept; | void addListener (Listener* const listenerToAdd) noexcept; | ||||
@@ -153,7 +150,7 @@ public: | |||||
void removeListener (Listener* const listenerToRemove) noexcept; | void removeListener (Listener* const listenerToRemove) noexcept; | ||||
private: | private: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
Array<MPEZone> zones; | Array<MPEZone> zones; | ||||
MidiRPNDetector rpnDetector; | MidiRPNDetector rpnDetector; | ||||
ListenerList<Listener> listeners; | ListenerList<Listener> listeners; | ||||
@@ -46,7 +46,7 @@ public: | |||||
@param source the input source to read from | @param source the input source to read from | ||||
@param backgroundThread a background thread that will be used for the | @param backgroundThread a background thread that will be used for the | ||||
background read-ahead. This object must not be deleted | 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! | have been deleted! | ||||
@param deleteSourceWhenDeleted if true, then the input source object will | @param deleteSourceWhenDeleted if true, then the input source object will | ||||
be deleted when this object is deleted | be deleted when this object is deleted | ||||
@@ -993,9 +993,9 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName) | |||||
class AudioSampleBufferSource : public PositionableAudioSource | class AudioSampleBufferSource : public PositionableAudioSource | ||||
{ | { | ||||
public: | public: | ||||
AudioSampleBufferSource (AudioSampleBuffer* audioBuffer, bool ownBuffer) | |||||
AudioSampleBufferSource (AudioSampleBuffer* audioBuffer, bool ownBuffer, bool playOnAllChannels) | |||||
: buffer (audioBuffer, ownBuffer), | : buffer (audioBuffer, ownBuffer), | ||||
position (0), looping (false) | |||||
position (0), looping (false), playAcrossAllChannels (playOnAllChannels) | |||||
{} | {} | ||||
//============================================================================== | //============================================================================== | ||||
@@ -1029,8 +1029,11 @@ public: | |||||
if (samplesToCopy > 0) | 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) | for (int i = 0; i < maxOutChannels; ++i) | ||||
bufferToFill.buffer->copyFrom (i, bufferToFill.startSample, *buffer, | bufferToFill.buffer->copyFrom (i, bufferToFill.startSample, *buffer, | ||||
@@ -1047,7 +1050,7 @@ private: | |||||
//============================================================================== | //============================================================================== | ||||
OptionalScopedPointer<AudioSampleBuffer> buffer; | OptionalScopedPointer<AudioSampleBuffer> buffer; | ||||
int position; | int position; | ||||
bool looping; | |||||
bool looping, playAcrossAllChannels; | |||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSampleBufferSource) | 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); | 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) | if (buffer != nullptr) | ||||
playSound (new AudioSampleBufferSource (buffer, deleteWhenFinished), true); | |||||
playSound (new AudioSampleBufferSource (buffer, deleteWhenFinished, playOnAllOutputChannels), true); | |||||
} | } | ||||
void AudioDeviceManager::playSound (PositionableAudioSource* audioSource, bool deleteWhenFinished) | 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, 0, soundLength / 10, 0.0f, 1.0f); | ||||
newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.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 | This will output the sound contained in an audio sample buffer. If | ||||
deleteWhenFinished is true then the audio sample buffer will be | deleteWhenFinished is true then the audio sample buffer will be | ||||
automatically deleted once the sound has finished playing. | 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. | /** Turns on level-measuring. | ||||
@@ -31,7 +31,12 @@ | |||||
#error "Incorrect use of JUCE cpp file" | #error "Incorrect use of JUCE cpp file" | ||||
#endif | #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" | #include "juce_audio_devices.h" | ||||
//============================================================================== | //============================================================================== | ||||
@@ -141,7 +146,6 @@ namespace juce | |||||
//============================================================================== | //============================================================================== | ||||
#if JUCE_MAC | #if JUCE_MAC | ||||
#include "../juce_core/native/juce_osx_ObjCHelpers.h" | |||||
#include "native/juce_mac_CoreAudio.cpp" | #include "native/juce_mac_CoreAudio.cpp" | ||||
#include "native/juce_mac_CoreMidi.cpp" | #include "native/juce_mac_CoreMidi.cpp" | ||||
@@ -160,8 +164,6 @@ namespace juce | |||||
//============================================================================== | //============================================================================== | ||||
#elif JUCE_WINDOWS | #elif JUCE_WINDOWS | ||||
#include "../juce_core/native/juce_win32_ComSmartPtr.h" | |||||
#include "../juce_events/native/juce_win32_HiddenMessageWindow.h" | |||||
#if JUCE_WASAPI | #if JUCE_WASAPI | ||||
#include "native/juce_win32_WASAPI.cpp" | #include "native/juce_win32_WASAPI.cpp" | ||||
@@ -203,7 +205,6 @@ namespace juce | |||||
//============================================================================== | //============================================================================== | ||||
#elif JUCE_ANDROID | #elif JUCE_ANDROID | ||||
#include "../juce_core/native/juce_android_JNIHelpers.h" | |||||
#include "native/juce_android_Audio.cpp" | #include "native/juce_android_Audio.cpp" | ||||
#include "native/juce_android_Midi.cpp" | #include "native/juce_android_Midi.cpp" | ||||
@@ -29,7 +29,7 @@ | |||||
#include "../juce_audio_basics/juce_audio_basics.h" | #include "../juce_audio_basics/juce_audio_basics.h" | ||||
#include "../juce_audio_formats/juce_audio_formats.h" | #include "../juce_audio_formats/juce_audio_formats.h" | ||||
//============================================================================= | |||||
//============================================================================== | |||||
/** Config: JUCE_ASIO | /** Config: JUCE_ASIO | ||||
Enables ASIO audio devices (MS Windows only). | Enables ASIO audio devices (MS Windows only). | ||||
Turning this on means that you'll need to have the Steinberg ASIO SDK installed | Turning this on means that you'll need to have the Steinberg ASIO SDK installed | ||||
@@ -90,7 +90,7 @@ | |||||
#endif | #endif | ||||
#endif | #endif | ||||
//============================================================================= | |||||
//============================================================================== | |||||
/** Config: JUCE_USE_CDREADER | /** Config: JUCE_USE_CDREADER | ||||
Enables the AudioCDReader class (on supported platforms). | Enables the AudioCDReader class (on supported platforms). | ||||
*/ | */ | ||||
@@ -105,7 +105,7 @@ | |||||
#define JUCE_USE_CDBURNER 0 | #define JUCE_USE_CDBURNER 0 | ||||
#endif | #endif | ||||
//============================================================================= | |||||
//============================================================================== | |||||
namespace juce | namespace juce | ||||
{ | { | ||||
@@ -195,25 +195,51 @@ public: | |||||
STREAM_MUSIC, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT, | STREAM_MUSIC, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT, | ||||
(jint) (minBufferSizeOut * numDeviceOutputChannels * sizeof (int16)), MODE_STREAM)); | (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; | isRunning = true; | ||||
} | |||||
else | 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) | 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 | 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) | if (isRunning) | ||||
@@ -368,7 +394,7 @@ public: | |||||
int minBufferSizeOut, minBufferSizeIn; | int minBufferSizeOut, minBufferSizeIn; | ||||
private: | private: | ||||
//================================================================================================== | |||||
//============================================================================== | |||||
CriticalSection callbackLock; | CriticalSection callbackLock; | ||||
AudioIODeviceCallback* callback; | AudioIODeviceCallback* callback; | ||||
jint sampleRate; | jint sampleRate; | ||||
@@ -145,12 +145,32 @@ public: | |||||
<< ", sampleRate = " << sampleRate); | << ", sampleRate = " << sampleRate); | ||||
if (numInputChannels > 0) | 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) | 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 | // pre-fill buffers | ||||
for (int i = 0; i < audioBuffersToEnqueue; ++i) | for (int i = 0; i < audioBuffersToEnqueue; ++i) | ||||
@@ -220,7 +240,7 @@ public: | |||||
} | } | ||||
private: | private: | ||||
//================================================================================================== | |||||
//============================================================================== | |||||
CriticalSection callbackLock; | CriticalSection callbackLock; | ||||
AudioIODeviceCallback* callback; | AudioIODeviceCallback* callback; | ||||
int actualBufferSize, sampleRate; | int actualBufferSize, sampleRate; | ||||
@@ -242,7 +262,7 @@ private: | |||||
defaultBufferSizeIsMultipleOfNative = 1 | defaultBufferSizeIsMultipleOfNative = 1 | ||||
}; | }; | ||||
//================================================================================================== | |||||
//============================================================================== | |||||
static String audioManagerGetProperty (const String& property) | static String audioManagerGetProperty (const String& property) | ||||
{ | { | ||||
const LocalRef<jstring> jProperty (javaString (property)); | const LocalRef<jstring> jProperty (javaString (property)); | ||||
@@ -281,7 +301,7 @@ private: | |||||
return androidHasSystemFeature ("android.hardware.audio.low_latency"); | return androidHasSystemFeature ("android.hardware.audio.low_latency"); | ||||
} | } | ||||
//================================================================================================== | |||||
//============================================================================== | |||||
AudioIODeviceCallback* setCallback (AudioIODeviceCallback* const newCallback) | AudioIODeviceCallback* setCallback (AudioIODeviceCallback* const newCallback) | ||||
{ | { | ||||
const ScopedLock sl (callbackLock); | const ScopedLock sl (callbackLock); | ||||
@@ -331,7 +351,7 @@ private: | |||||
DBG ("Unable to set audio thread priority: priority is still " << priority); | DBG ("Unable to set audio thread priority: priority is still " << priority); | ||||
} | } | ||||
//================================================================================================== | |||||
//============================================================================== | |||||
struct Engine | struct Engine | ||||
{ | { | ||||
Engine() | Engine() | ||||
@@ -400,7 +420,7 @@ private: | |||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Engine) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Engine) | ||||
}; | }; | ||||
//================================================================================================== | |||||
//============================================================================== | |||||
struct BufferList | struct BufferList | ||||
{ | { | ||||
BufferList (const int numChannels_, const int numBuffers_, const int numSamples_) | BufferList (const int numChannels_, const int numBuffers_, const int numSamples_) | ||||
@@ -444,7 +464,7 @@ private: | |||||
WaitableEvent dataArrived; | WaitableEvent dataArrived; | ||||
}; | }; | ||||
//================================================================================================== | |||||
//============================================================================== | |||||
struct Player | struct Player | ||||
{ | { | ||||
Player (int numChannels, int sampleRate, Engine& engine, int playerNumBuffers, int playerBufferSize) | Player (int numChannels, int sampleRate, Engine& engine, int playerNumBuffers, int playerBufferSize) | ||||
@@ -559,7 +579,7 @@ private: | |||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Player) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Player) | ||||
}; | }; | ||||
//================================================================================================== | |||||
//============================================================================== | |||||
struct Recorder | struct Recorder | ||||
{ | { | ||||
Recorder (int numChannels, int sampleRate, Engine& engine, const int numBuffers, const int numSamples) | Recorder (int numChannels, int sampleRate, Engine& engine, const int numBuffers, const int numSamples) | ||||
@@ -24,7 +24,9 @@ | |||||
class iOSAudioIODevice; | class iOSAudioIODevice; | ||||
//================================================================================================== | |||||
static const char* const iOSAudioDeviceName = "iOS Audio"; | |||||
//============================================================================== | |||||
struct AudioSessionHolder | struct AudioSessionHolder | ||||
{ | { | ||||
AudioSessionHolder(); | AudioSessionHolder(); | ||||
@@ -74,7 +76,7 @@ bool getNotificationValueForKey (NSNotification* notification, NSString* key, NS | |||||
} // juce namespace | } // juce namespace | ||||
//================================================================================================== | |||||
//============================================================================== | |||||
@interface iOSAudioSessionNative : NSObject | @interface iOSAudioSessionNative : NSObject | ||||
{ | { | ||||
@private | @private | ||||
@@ -178,7 +180,7 @@ bool getNotificationValueForKey (NSNotification* notification, NSString* key, NS | |||||
@end | @end | ||||
//================================================================================================== | |||||
//============================================================================== | |||||
namespace juce { | namespace juce { | ||||
#ifndef JUCE_IOS_AUDIO_LOGGING | #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); } | #define JUCE_NSERROR_CHECK(X) { NSError* error = nil; X; logNSError (error); } | ||||
//================================================================================================== | |||||
//============================================================================== | |||||
class iOSAudioIODevice : public AudioIODevice | class iOSAudioIODevice : public AudioIODevice | ||||
{ | { | ||||
public: | public: | ||||
iOSAudioIODevice (const String& deviceName) : AudioIODevice (deviceName, "Audio") | |||||
iOSAudioIODevice (const String& deviceName) | |||||
: AudioIODevice (deviceName, iOSAudioDeviceName) | |||||
{ | { | ||||
sessionHolder->activeDevices.add (this); | sessionHolder->activeDevices.add (this); | ||||
updateSampleRateAndAudioInput(); | updateSampleRateAndAudioInput(); | ||||
@@ -350,13 +353,14 @@ public: | |||||
{ | { | ||||
isRunning = false; | isRunning = false; | ||||
setAudioSessionActive (false); | |||||
if (audioUnit != 0) | if (audioUnit != 0) | ||||
{ | { | ||||
AudioOutputUnitStart (audioUnit); | |||||
AudioComponentInstanceDispose (audioUnit); | AudioComponentInstanceDispose (audioUnit); | ||||
audioUnit = 0; | audioUnit = 0; | ||||
} | } | ||||
setAudioSessionActive (false); | |||||
} | } | ||||
} | } | ||||
@@ -463,13 +467,13 @@ public: | |||||
AudioOutputUnitStart (audioUnit); | AudioOutputUnitStart (audioUnit); | ||||
} | } | ||||
if (callback) | |||||
if (callback != nullptr) | |||||
callback->audioDeviceAboutToStart (this); | callback->audioDeviceAboutToStart (this); | ||||
} | } | ||||
} | } | ||||
private: | private: | ||||
//================================================================================================== | |||||
//============================================================================== | |||||
SharedResourcePointer<AudioSessionHolder> sessionHolder; | SharedResourcePointer<AudioSessionHolder> sessionHolder; | ||||
CriticalSection callbackLock; | CriticalSection callbackLock; | ||||
NSTimeInterval sampleRate = 0; | NSTimeInterval sampleRate = 0; | ||||
@@ -505,7 +509,7 @@ private: | |||||
} | } | ||||
} | } | ||||
//================================================================================================== | |||||
//============================================================================== | |||||
OSStatus process (AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time, | OSStatus process (AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time, | ||||
const UInt32 numFrames, AudioBufferList* data) | const UInt32 numFrames, AudioBufferList* data) | ||||
{ | { | ||||
@@ -592,21 +596,22 @@ private: | |||||
auto session = [AVAudioSession sharedInstance]; | auto session = [AVAudioSession sharedInstance]; | ||||
sampleRate = session.sampleRate; | sampleRate = session.sampleRate; | ||||
audioInputIsAvailable = session.isInputAvailable; | 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() | 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, | static OSStatus processStatic (void* client, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time, | ||||
UInt32 /*busNumber*/, UInt32 numFrames, AudioBufferList* data) | UInt32 /*busNumber*/, UInt32 numFrames, AudioBufferList* data) | ||||
{ | { | ||||
@@ -614,7 +619,7 @@ private: | |||||
return static_cast<iOSAudioIODevice*> (client)->process (flags, time, numFrames, data); | return static_cast<iOSAudioIODevice*> (client)->process (flags, time, numFrames, data); | ||||
} | } | ||||
//================================================================================================== | |||||
//============================================================================== | |||||
void resetFormat (const int numChannels) noexcept | void resetFormat (const int numChannels) noexcept | ||||
{ | { | ||||
zerostruct (format); | zerostruct (format); | ||||
@@ -712,10 +717,10 @@ private: | |||||
class iOSAudioIODeviceType : public AudioIODeviceType | class iOSAudioIODeviceType : public AudioIODeviceType | ||||
{ | { | ||||
public: | public: | ||||
iOSAudioIODeviceType() : AudioIODeviceType ("iOS Audio") {} | |||||
iOSAudioIODeviceType() : AudioIODeviceType (iOSAudioDeviceName) {} | |||||
void scanForDevices() {} | 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 getDefaultDeviceIndex (bool /*forInput*/) const { return 0; } | ||||
int getIndexOfDevice (AudioIODevice* d, bool /*asInput*/) const { return d != nullptr ? 0 : -1; } | int getIndexOfDevice (AudioIODevice* d, bool /*asInput*/) const { return d != nullptr ? 0 : -1; } | ||||
bool hasSeparateInputsAndOutputs() const { return false; } | bool hasSeparateInputsAndOutputs() const { return false; } | ||||
@@ -738,7 +743,7 @@ AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() | |||||
return new iOSAudioIODeviceType(); | return new iOSAudioIODeviceType(); | ||||
} | } | ||||
//================================================================================================== | |||||
//============================================================================== | |||||
AudioSessionHolder::AudioSessionHolder() { nativeSession = [[iOSAudioSessionNative alloc] init: this]; } | AudioSessionHolder::AudioSessionHolder() { nativeSession = [[iOSAudioSessionNative alloc] init: this]; } | ||||
AudioSessionHolder::~AudioSessionHolder() { [nativeSession release]; } | AudioSessionHolder::~AudioSessionHolder() { [nativeSession release]; } | ||||
@@ -31,6 +31,12 @@ | |||||
#ifdef __clang__ | #ifdef __clang__ | ||||
#pragma clang diagnostic push | #pragma clang diagnostic push | ||||
#pragma clang diagnostic ignored "-Wnonnull" // aovid some spurious 10.11 SDK warnings | #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 | #endif | ||||
//============================================================================== | //============================================================================== | ||||
@@ -382,65 +388,84 @@ public: | |||||
return 0; | 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; | AudioObjectPropertyAddress pa; | ||||
pa.mScope = kAudioObjectPropertyScopeWildcard; | pa.mScope = kAudioObjectPropertyScopeWildcard; | ||||
pa.mElement = kAudioObjectPropertyElementMaster; | pa.mElement = kAudioObjectPropertyElementMaster; | ||||
pa.mSelector = kAudioDevicePropertyDeviceIsAlive; | |||||
UInt32 isAlive; | |||||
UInt32 isAlive = 0; | |||||
UInt32 size = sizeof (isAlive); | 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; | Array<CallbackDetailsForChannel> newInChans, newOutChans; | ||||
StringArray newInNames (getChannelInfo (true, newInChans)); | StringArray newInNames (getChannelInfo (true, newInChans)); | ||||
StringArray newOutNames (getChannelInfo (false, newOutChans)); | 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(); | stopTimer(); | ||||
const double oldSampleRate = sampleRate; | const double oldSampleRate = sampleRate; | ||||
const int oldBufferSize = bufferSize; | const int oldBufferSize = bufferSize; | ||||
updateDetailsFromDevice(); | |||||
if (oldBufferSize != bufferSize || oldSampleRate != sampleRate) | |||||
if (! updateDetailsFromDevice()) | |||||
owner.stop(); | |||||
else if (oldBufferSize != bufferSize || oldSampleRate != sampleRate) | |||||
owner.restart(); | owner.restart(); | ||||
} | } | ||||
@@ -320,9 +320,9 @@ public: | |||||
classId (clsID), | classId (clsID), | ||||
inputLatency (0), | inputLatency (0), | ||||
outputLatency (0), | outputLatency (0), | ||||
minSize (0), maxSize (0), | |||||
preferredSize (0), | |||||
granularity (0), | |||||
minBufferSize (0), maxBufferSize (0), | |||||
preferredBufferSize (0), | |||||
bufferGranularity (0), | |||||
numClockSources (0), | numClockSources (0), | ||||
currentBlockSizeSamples (0), | currentBlockSizeSamples (0), | ||||
currentBitDepth (16), | currentBitDepth (16), | ||||
@@ -403,7 +403,7 @@ public: | |||||
Array<double> getAvailableSampleRates() override { return sampleRates; } | Array<double> getAvailableSampleRates() override { return sampleRates; } | ||||
Array<int> getAvailableBufferSizes() override { return bufferSizes; } | Array<int> getAvailableBufferSizes() override { return bufferSizes; } | ||||
int getDefaultBufferSize() override { return preferredSize; } | |||||
int getDefaultBufferSize() override { return preferredBufferSize; } | |||||
String open (const BigInteger& inputChannels, | String open (const BigInteger& inputChannels, | ||||
const BigInteger& outputChannels, | const BigInteger& outputChannels, | ||||
@@ -469,10 +469,10 @@ public: | |||||
removeCurrentDriver(); | removeCurrentDriver(); | ||||
loadDriver(); | 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; | needToReset = false; | ||||
} | } | ||||
@@ -489,7 +489,7 @@ public: | |||||
if (err != ASE_OK) | if (err != ASE_OK) | ||||
{ | { | ||||
currentBlockSizeSamples = preferredSize; | |||||
currentBlockSizeSamples = preferredBufferSize; | |||||
JUCE_ASIO_LOG_ERROR ("create buffers 2", err); | JUCE_ASIO_LOG_ERROR ("create buffers 2", err); | ||||
asioObject->disposeBuffers(); | asioObject->disposeBuffers(); | ||||
@@ -561,8 +561,7 @@ public: | |||||
} | } | ||||
readLatencies(); | readLatencies(); | ||||
asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity); | |||||
refreshBufferSizes(); | |||||
deviceIsOpen = true; | deviceIsOpen = true; | ||||
JUCE_ASIO_LOG ("starting"); | JUCE_ASIO_LOG ("starting"); | ||||
@@ -762,7 +761,7 @@ private: | |||||
Array<double> sampleRates; | Array<double> sampleRates; | ||||
Array<int> bufferSizes; | Array<int> bufferSizes; | ||||
long inputLatency, outputLatency; | long inputLatency, outputLatency; | ||||
long minSize, maxSize, preferredSize, granularity; | |||||
long minBufferSize, maxBufferSize, preferredBufferSize, bufferGranularity; | |||||
ASIOClockSource clocks[32]; | ASIOClockSource clocks[32]; | ||||
int numClockSources; | 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; | 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; | shouldUsePreferredSize = true; | ||||
if (bufferSizeSamples < minSize || bufferSizeSamples > maxSize) | |||||
if (bufferSizeSamples < minBufferSize || bufferSizeSamples > maxBufferSize) | |||||
shouldUsePreferredSize = true; | shouldUsePreferredSize = true; | ||||
preferredSize = newPreferredSize; | |||||
preferredBufferSize = newPreferredSize; | |||||
} | } | ||||
// unfortunate workaround for certain drivers which crash if you make | // unfortunate workaround for certain drivers which crash if you make | ||||
@@ -855,11 +858,11 @@ private: | |||||
if (shouldUsePreferredSize) | if (shouldUsePreferredSize) | ||||
{ | { | ||||
JUCE_ASIO_LOG ("Using preferred size for buffer.."); | JUCE_ASIO_LOG ("Using preferred size for buffer.."); | ||||
long err = asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity); | |||||
long err = refreshBufferSizes(); | |||||
if (err == ASE_OK) | if (err == ASE_OK) | ||||
{ | { | ||||
bufferSizeSamples = (int) preferredSize; | |||||
bufferSizeSamples = (int) preferredBufferSize; | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -1082,8 +1085,8 @@ private: | |||||
if (i < 2) | if (i < 2) | ||||
{ | { | ||||
// clear the channels that are used with the dummy stuff | // 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); | inputFormat.calloc (chansToAllocate); | ||||
outputFormat.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(); | double currentRate = getSampleRate(); | ||||
@@ -1226,8 +1229,8 @@ private: | |||||
updateSampleRates(); | 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(); | readLatencies(); | ||||
// start and stop because cubase does it.. | // start and stop because cubase does it.. | ||||
@@ -1419,6 +1422,12 @@ private: | |||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ASIOAudioIODevice) | 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 | class ASIOAudioIODeviceType : public AudioIODeviceType | ||||
{ | { | ||||
@@ -112,6 +112,11 @@ bool check (HRESULT hr) | |||||
#define JUCE_COMCLASS(name, guid) struct __declspec (uuid (guid)) name | #define JUCE_COMCLASS(name, guid) struct __declspec (uuid (guid)) name | ||||
#endif | #endif | ||||
#if JUCE_MINGW && defined (KSDATAFORMAT_SUBTYPE_PCM) | |||||
#undef KSDATAFORMAT_SUBTYPE_PCM | |||||
#undef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT | |||||
#endif | |||||
#ifndef KSDATAFORMAT_SUBTYPE_PCM | #ifndef KSDATAFORMAT_SUBTYPE_PCM | ||||
#define KSDATAFORMAT_SUBTYPE_PCM uuidFromString ("00000001-0000-0010-8000-00aa00389b71") | #define KSDATAFORMAT_SUBTYPE_PCM uuidFromString ("00000001-0000-0010-8000-00aa00389b71") | ||||
#define KSDATAFORMAT_SUBTYPE_IEEE_FLOAT uuidFromString ("00000003-0000-0010-8000-00aa00389b71") | #define KSDATAFORMAT_SUBTYPE_IEEE_FLOAT uuidFromString ("00000003-0000-0010-8000-00aa00389b71") | ||||
@@ -33,7 +33,7 @@ | |||||
#ifndef FLAC__PRIVATE__METADATA_H | #ifndef FLAC__PRIVATE__METADATA_H | ||||
#define 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 | /* 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 | * 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) | static StringPairArray parseMidiChunk (InputStream& input, int64 size) | ||||
@@ -288,7 +297,7 @@ struct CoreAudioFormatMetatdata | |||||
} | } | ||||
else if (chunkHeader.chunkType == chunkName ("uuid")) | else if (chunkHeader.chunkType == chunkName ("uuid")) | ||||
{ | { | ||||
UserDefinedChunk userDefinedChunk (input, chunkHeader.chunkSize); | |||||
metadataValues.addArray (parseUserDefinedChunk (input, chunkHeader.chunkSize)); | |||||
} | } | ||||
else if (chunkHeader.chunkType == chunkName ("data")) | else if (chunkHeader.chunkType == chunkName ("data")) | ||||
{ | { | ||||
@@ -27,15 +27,11 @@ | |||||
} // (juce namespace) | } // (juce namespace) | ||||
#if ! JUCE_WINDOWS | #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/Movies.h> | ||||
#include <QuickTime/QTML.h> | #include <QuickTime/QTML.h> | ||||
#include <QuickTime/QuickTimeComponents.h> | #include <QuickTime/QuickTimeComponents.h> | ||||
#include <QuickTime/MediaHandlers.h> | #include <QuickTime/MediaHandlers.h> | ||||
#include <QuickTime/ImageCodec.h> | #include <QuickTime/ImageCodec.h> | ||||
#undef Point | |||||
#undef Component | |||||
#else | #else | ||||
#if JUCE_MSVC | #if JUCE_MSVC | ||||
#pragma warning (push) | #pragma warning (push) | ||||
@@ -31,19 +31,18 @@ | |||||
#error "Incorrect use of JUCE cpp file" | #error "Incorrect use of JUCE cpp file" | ||||
#endif | #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" | #include "juce_audio_formats.h" | ||||
//============================================================================== | //============================================================================== | ||||
#if JUCE_MAC | #if JUCE_MAC | ||||
#define Point CarbonDummyPointName | |||||
#define Component CarbonDummyCompName | |||||
#if JUCE_QUICKTIME | #if JUCE_QUICKTIME | ||||
#import <QTKit/QTKit.h> | #import <QTKit/QTKit.h> | ||||
#endif | #endif | ||||
#include <AudioToolbox/AudioToolbox.h> | #include <AudioToolbox/AudioToolbox.h> | ||||
#undef Component | |||||
#undef Point | |||||
#elif JUCE_IOS | #elif JUCE_IOS | ||||
#import <AudioToolbox/AudioToolbox.h> | #import <AudioToolbox/AudioToolbox.h> | ||||
@@ -85,14 +84,9 @@ namespace juce | |||||
{ | { | ||||
#if JUCE_ANDROID | #if JUCE_ANDROID | ||||
#include "../juce_core/native/juce_android_JNIHelpers.h" | |||||
#undef JUCE_QUICKTIME | #undef JUCE_QUICKTIME | ||||
#endif | #endif | ||||
#if JUCE_WINDOWS | |||||
#include "../juce_core/native/juce_win32_ComSmartPtr.h" | |||||
#endif | |||||
#include "format/juce_AudioFormat.cpp" | #include "format/juce_AudioFormat.cpp" | ||||
#include "format/juce_AudioFormatManager.cpp" | #include "format/juce_AudioFormatManager.cpp" | ||||
#include "format/juce_AudioFormatReader.cpp" | #include "format/juce_AudioFormatReader.cpp" | ||||
@@ -27,7 +27,7 @@ | |||||
#include "../juce_audio_basics/juce_audio_basics.h" | #include "../juce_audio_basics/juce_audio_basics.h" | ||||
//============================================================================= | |||||
//============================================================================== | |||||
/** Config: JUCE_USE_FLAC | /** Config: JUCE_USE_FLAC | ||||
Enables the FLAC audio codec classes (available on all platforms). | 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 | 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 | #define JUCE_USE_WINDOWS_MEDIA_FORMAT 0 | ||||
#endif | #endif | ||||
//============================================================================= | |||||
//============================================================================== | |||||
namespace juce | namespace juce | ||||
{ | { | ||||
@@ -29,8 +29,8 @@ | |||||
#include "../utility/juce_IncludeSystemHeaders.h" | #include "../utility/juce_IncludeSystemHeaders.h" | ||||
#include "../utility/juce_IncludeModuleHeaders.h" | #include "../utility/juce_IncludeModuleHeaders.h" | ||||
#include "../utility/juce_WindowsHooks.h" | |||||
#include "../utility/juce_PluginBusUtilities.h" | #include "../utility/juce_PluginBusUtilities.h" | ||||
#undef Component | |||||
#ifdef __clang__ | #ifdef __clang__ | ||||
#pragma clang diagnostic push | #pragma clang diagnostic push | ||||
@@ -40,7 +40,7 @@ | |||||
#ifdef _MSC_VER | #ifdef _MSC_VER | ||||
#pragma warning (push) | #pragma warning (push) | ||||
#pragma warning (disable : 4127) | |||||
#pragma warning (disable : 4127 4512) | |||||
#endif | #endif | ||||
#include "AAX_Exports.cpp" | #include "AAX_Exports.cpp" | ||||
@@ -93,8 +93,6 @@ | |||||
#undef check | #undef check | ||||
using juce::Component; | |||||
const int32_t juceChunkType = 'juce'; | const int32_t juceChunkType = 'juce'; | ||||
//============================================================================== | //============================================================================== | ||||
@@ -115,6 +113,15 @@ struct AAXClasses | |||||
return AAX::IsParameterIDEqual (paramID, cDefaultMasterBypassID) != 0; | 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 | 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 | // 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; | 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; | class JuceAAX_Processor; | ||||
@@ -381,7 +406,7 @@ struct AAXClasses | |||||
} | } | ||||
private: | private: | ||||
struct ContentWrapperComponent : public juce::Component | |||||
struct ContentWrapperComponent : public Component | |||||
{ | { | ||||
ContentWrapperComponent (JuceAAX_GUI& gui, AudioProcessor& plugin) | ContentWrapperComponent (JuceAAX_GUI& gui, AudioProcessor& plugin) | ||||
: owner (gui) | : owner (gui) | ||||
@@ -449,6 +474,10 @@ struct AAXClasses | |||||
ScopedPointer<AudioProcessorEditor> pluginEditor; | ScopedPointer<AudioProcessorEditor> pluginEditor; | ||||
JuceAAX_GUI& owner; | JuceAAX_GUI& owner; | ||||
#if JUCE_WINDOWS | |||||
WindowsHooks hooks; | |||||
#endif | |||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentWrapperComponent) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentWrapperComponent) | ||||
}; | }; | ||||
@@ -839,7 +868,7 @@ struct AAXClasses | |||||
jassert (idx < (mainNumIns + 1)); | jassert (idx < (mainNumIns + 1)); | ||||
if (idx < mainNumIns) | if (idx < mainNumIns) | ||||
return inputs[idx]; | |||||
return inputs[inputLayoutMap[idx]]; | |||||
return (sidechain != -1 ? inputs[sidechain] : sideChainBuffer.getData()); | 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 mainNumIns = numIns > 0 ? pluginInstance->busArrangement.inputBuses.getReference (0).channels.size() : 0; | ||||
const int sidechain = busUtils.getNumEnabledBuses (true) >= 2 ? sideChainBufferIdx : -1; | 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) | if (numOuts >= numIns) | ||||
{ | { | ||||
for (int i = 0; i < numOuts; ++i) | |||||
channels[i] = outputs[outputLayoutMap[i]]; | |||||
for (int i = 0; i < numIns; ++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 | 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) | 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) | for (int i = numOuts; i < numIns; ++i) | ||||
channels[i] = const_cast<float*> (getAudioBufferForInput (inputs, sidechain, mainNumIns, 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) | || (busUtils.getBusCount (false) > 0 && busUtils.getChannelSet (false, 0) != outputSet) | ||||
|| (hasSidechain && busUtils.getNumChannels(true, 1) != 1)) | || (hasSidechain && busUtils.getNumChannels(true, 1) != 1)) | ||||
return AAX_ERROR_UNIMPLEMENTED; | return AAX_ERROR_UNIMPLEMENTED; | ||||
rebuildChannelMapArrays (true); | |||||
rebuildChannelMapArrays (false); | |||||
#endif | #endif | ||||
audioProcessor.setRateAndBufferSizeDetails (sampleRate, maxBufferSize); | audioProcessor.setRateAndBufferSizeDetails (sampleRate, maxBufferSize); | ||||
@@ -1071,6 +1109,30 @@ struct AAXClasses | |||||
return AAX_SUCCESS; | 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; | ScopedJuceInitialiser_GUI libraryInitialiser; | ||||
ScopedPointer<AudioProcessor> pluginInstance; | ScopedPointer<AudioProcessor> pluginInstance; | ||||
@@ -1082,6 +1144,7 @@ struct AAXClasses | |||||
int lastBufferSize, maxBufferSize; | int lastBufferSize, maxBufferSize; | ||||
bool hasSidechain; | bool hasSidechain; | ||||
HeapBlock<float> sideChainBuffer; | HeapBlock<float> sideChainBuffer; | ||||
Array<int> inputLayoutMap, outputLayoutMap; | |||||
struct ChunkMemoryBlock : public ReferenceCountedObject | struct ChunkMemoryBlock : public ReferenceCountedObject | ||||
{ | { | ||||
@@ -1308,6 +1371,10 @@ struct AAXClasses | |||||
properties->AddProperty (AAX_eProperty_Constraint_MultiMonoSupport, true); | properties->AddProperty (AAX_eProperty_Constraint_MultiMonoSupport, true); | ||||
#endif | #endif | ||||
#if JucePlugin_AAXDisableDynamicProcessing | |||||
properties->AddProperty (AAX_eProperty_Constraint_AlwaysProcess, true); | |||||
#endif | |||||
if (enableAuxBusesForCurrentFormat (busUtils, inputLayout, outputLayout)) | if (enableAuxBusesForCurrentFormat (busUtils, inputLayout, outputLayout)) | ||||
{ | { | ||||
check (desc.AddSideChainIn (JUCEAlgorithmIDs::sideChainBuffers)); | 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*); | ||||
AAX_Result JUCE_CDECL GetEffectDescriptions (AAX_ICollection* collection) | AAX_Result JUCE_CDECL GetEffectDescriptions (AAX_ICollection* collection) | ||||
@@ -48,22 +48,7 @@ | |||||
#include <AudioUnit/AudioUnit.h> | #include <AudioUnit/AudioUnit.h> | ||||
#include <AudioToolbox/AudioUnitUtilities.h> | #include <AudioToolbox/AudioUnitUtilities.h> | ||||
#include <CoreMIDI/MIDIServices.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" | #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 | /** 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 | 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 | #endif | ||||
#if BUILD_AU_CARBON_UI | #if BUILD_AU_CARBON_UI | ||||
#undef Button | |||||
#define Point CarbonDummyPointName | |||||
#include "CoreAudioUtilityClasses/AUCarbonViewBase.h" | #include "CoreAudioUtilityClasses/AUCarbonViewBase.h" | ||||
#undef Point | |||||
#endif | #endif | ||||
#ifdef __clang__ | #ifdef __clang__ | ||||
@@ -89,12 +71,12 @@ | |||||
#endif | #endif | ||||
#define JUCE_MAC_WINDOW_VISIBITY_BODGE 1 | #define JUCE_MAC_WINDOW_VISIBITY_BODGE 1 | ||||
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1 | |||||
#include "../utility/juce_IncludeModuleHeaders.h" | #include "../utility/juce_IncludeModuleHeaders.h" | ||||
#include "../utility/juce_FakeMouseMoveGenerator.h" | #include "../utility/juce_FakeMouseMoveGenerator.h" | ||||
#include "../utility/juce_CarbonVisibility.h" | #include "../utility/juce_CarbonVisibility.h" | ||||
#include "../utility/juce_PluginBusUtilities.h" | #include "../utility/juce_PluginBusUtilities.h" | ||||
#include "../../juce_core/native/juce_osx_ObjCHelpers.h" | |||||
//============================================================================== | //============================================================================== | ||||
static Array<void*> activePlugins, activeUIs; | static Array<void*> activePlugins, activeUIs; | ||||
@@ -976,7 +958,7 @@ public: | |||||
} | } | ||||
//============================================================================== | //============================================================================== | ||||
ComponentResult Render (AudioUnitRenderActionFlags &ioActionFlags, | |||||
ComponentResult Render (AudioUnitRenderActionFlags& ioActionFlags, | |||||
const AudioTimeStamp& inTimeStamp, | const AudioTimeStamp& inTimeStamp, | ||||
const UInt32 nFrames) override | const UInt32 nFrames) override | ||||
{ | { | ||||
@@ -988,7 +970,7 @@ public: | |||||
for (unsigned int i = 0; i < numInputBuses; ++i) | for (unsigned int i = 0; i < numInputBuses; ++i) | ||||
{ | { | ||||
AudioUnitRenderActionFlags flags = ioActionFlags; | AudioUnitRenderActionFlags flags = ioActionFlags; | ||||
AUInputElement* input = GetInput (i); | |||||
AUInputElement* input = GetInput (i); | |||||
OSStatus result = input->PullInput (flags, inTimeStamp, i, nFrames); | OSStatus result = input->PullInput (flags, inTimeStamp, i, nFrames); | ||||
@@ -1025,20 +1007,23 @@ public: | |||||
for (unsigned int chIdx = 0; chIdx < numOutChannels; ++chIdx) | 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); | 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) | if (chIdx < numInChannels) | ||||
{ | { | ||||
const AudioBufferList& inBuffer = input->GetBufferList(); | const AudioBufferList& inBuffer = input->GetBufferList(); | ||||
const bool isInputInterleaved = (numInChannels > 1) && (inBuffer.mNumberBuffers == 1); | 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) | if (isInputInterleaved) | ||||
{ | { | ||||
for (unsigned int i = 0; i < nFrames; ++i) | for (unsigned int i = 0; i < nFrames; ++i) | ||||
{ | { | ||||
outData [i] = inData[chIdx]; | |||||
outData [i] = inData[mappedInChIdx]; | |||||
inData += numInChannels; | inData += numInChannels; | ||||
} | } | ||||
} | } | ||||
@@ -1056,15 +1041,17 @@ public: | |||||
for (unsigned int chIdx = 0; chIdx < numInChannels; ++chIdx) | 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++] | float* buffer = isInputInterleaved ? scratchBuffers[scratchIdx++] | ||||
: static_cast<float*> (inBuffer.mBuffers[chIdx].mData); | |||||
: static_cast<float*> (inBuffer.mBuffers[mappedInChIdx].mData); | |||||
if (isInputInterleaved) | if (isInputInterleaved) | ||||
{ | { | ||||
const float* inData = static_cast<float*> (inBuffer.mBuffers[0].mData); | const float* inData = static_cast<float*> (inBuffer.mBuffers[0].mData); | ||||
for (unsigned int i = 0; i < nFrames; ++i) | for (unsigned int i = 0; i < nFrames; ++i) | ||||
{ | { | ||||
buffer [i] = inData [chIdx]; | |||||
buffer [i] = inData [mappedInChIdx]; | |||||
inData += numInChannels; | inData += numInChannels; | ||||
} | } | ||||
} | } | ||||
@@ -1114,32 +1101,45 @@ public: | |||||
const unsigned int numInChannels = (input != nullptr ? input ->GetStreamFormat().mChannelsPerFrame : 0); | const unsigned int numInChannels = (input != nullptr ? input ->GetStreamFormat().mChannelsPerFrame : 0); | ||||
const unsigned int numOutChannels = (output != nullptr ? output->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(); | midiEvents.clear(); | ||||
} | } | ||||
#if ! JucePlugin_SilenceInProducesSilenceOut | |||||
ioActionFlags &= (AudioUnitRenderActionFlags) ~kAudioUnitRenderAction_OutputIsSilence; | |||||
#else | |||||
ignoreUnused (ioActionFlags); | |||||
#endif | |||||
return noErr; | return noErr; | ||||
} | } | ||||
@@ -1545,6 +1539,7 @@ private: | |||||
Array<AUChannelInfo> channelInfo; | Array<AUChannelInfo> channelInfo; | ||||
Array<Array<AudioChannelLayoutTag> > supportedInputLayouts, supportedOutputLayouts; | Array<Array<AudioChannelLayoutTag> > supportedInputLayouts, supportedOutputLayouts; | ||||
Array<AudioChannelLayoutTag> currentInputLayout, currentOutputLayout; | Array<AudioChannelLayoutTag> currentInputLayout, currentOutputLayout; | ||||
Array<Array<int> > inputLayoutMap, outputLayoutMap; | |||||
//============================================================================== | //============================================================================== | ||||
static OSStatus scopeToDirection (AudioUnitScope scope, bool& isInput) noexcept | static OSStatus scopeToDirection (AudioUnitScope scope, bool& isInput) noexcept | ||||
@@ -1601,13 +1596,22 @@ private: | |||||
OSStatus syncProcessorWithAudioUnit() | OSStatus syncProcessorWithAudioUnit() | ||||
{ | { | ||||
OSStatus err; | 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 numInputElements = static_cast<int> (GetScope(kAudioUnitScope_Input). GetNumberOfElements()); | ||||
const int numOutputElements = static_cast<int> (GetScope(kAudioUnitScope_Output).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; | 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 ((err = syncProcessorWithAudioUnitForBus (false, i)) != noErr) return err; | ||||
if (numInputElements != busUtils.getNumEnabledBuses (true) || numOutputElements != busUtils.getNumEnabledBuses (false)) | if (numInputElements != busUtils.getNumEnabledBuses (true) || numOutputElements != busUtils.getNumEnabledBuses (false)) | ||||
@@ -1626,23 +1630,29 @@ private: | |||||
//============================================================================== | //============================================================================== | ||||
OSStatus syncProcessorWithAudioUnitForBus (bool isInput, int busNr) | 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) | 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 | static AudioChannelSet::ChannelType CoreAudioChannelLabelToJuceType (AudioChannelLabel label) noexcept | ||||
{ | { | ||||
if (label >= kAudioChannelLabel_Discrete_0 && label <= kAudioChannelLabel_Discrete_65535) | if (label >= kAudioChannelLabel_Discrete_0 && label <= kAudioChannelLabel_Discrete_65535) | ||||
@@ -1911,10 +1965,58 @@ private: | |||||
case kAudioChannelLabel_RearSurroundRight: return AudioChannelSet::topRearRight; | case kAudioChannelLabel_RearSurroundRight: return AudioChannelSet::topRearRight; | ||||
case kAudioChannelLabel_TopBackCenter: return AudioChannelSet::topRearCentre; | case kAudioChannelLabel_TopBackCenter: return AudioChannelSet::topRearCentre; | ||||
case kAudioChannelLabel_LFE2: return AudioChannelSet::subbass2; | 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; | 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 | static AudioChannelSet CoreAudioChannelBitmapToJuceType (UInt32 bitmap) noexcept | ||||
{ | { | ||||
AudioChannelSet set; | AudioChannelSet set; | ||||
@@ -1962,6 +2064,7 @@ private: | |||||
{ | { | ||||
switch (tag) | switch (tag) | ||||
{ | { | ||||
case kAudioChannelLayoutTag_Unknown: return AudioChannelSet::disabled(); | |||||
case kAudioChannelLayoutTag_Mono: return AudioChannelSet::mono(); | case kAudioChannelLayoutTag_Mono: return AudioChannelSet::mono(); | ||||
case kAudioChannelLayoutTag_Stereo: | case kAudioChannelLayoutTag_Stereo: | ||||
case kAudioChannelLayoutTag_StereoHeadphones: | case kAudioChannelLayoutTag_StereoHeadphones: | ||||
@@ -1979,6 +2082,10 @@ private: | |||||
case kAudioChannelLayoutTag_MPEG_7_1_C: return AudioChannelSet::create7point1(); | case kAudioChannelLayoutTag_MPEG_7_1_C: return AudioChannelSet::create7point1(); | ||||
case kAudioChannelLayoutTag_AudioUnit_7_0_Front: return AudioChannelSet::createFront7point0(); | case kAudioChannelLayoutTag_AudioUnit_7_0_Front: return AudioChannelSet::createFront7point0(); | ||||
case kAudioChannelLayoutTag_AudioUnit_7_1_Front: return AudioChannelSet::createFront7point1(); | 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) | if (int numChannels = static_cast<int> (tag) & 0xffff) | ||||
@@ -1993,6 +2100,8 @@ private: | |||||
{ | { | ||||
if (set == AudioChannelSet::mono()) return kAudioChannelLayoutTag_Mono; | if (set == AudioChannelSet::mono()) return kAudioChannelLayoutTag_Mono; | ||||
if (set == AudioChannelSet::stereo()) return kAudioChannelLayoutTag_Stereo; | 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::quadraphonic()) return kAudioChannelLayoutTag_Quadraphonic; | ||||
if (set == AudioChannelSet::pentagonal()) return kAudioChannelLayoutTag_Pentagonal; | if (set == AudioChannelSet::pentagonal()) return kAudioChannelLayoutTag_Pentagonal; | ||||
if (set == AudioChannelSet::hexagonal()) return kAudioChannelLayoutTag_Hexagonal; | 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::create7point1()) return kAudioChannelLayoutTag_MPEG_7_1_C; | ||||
if (set == AudioChannelSet::createFront7point0()) return kAudioChannelLayoutTag_AudioUnit_7_0_Front; | if (set == AudioChannelSet::createFront7point0()) return kAudioChannelLayoutTag_AudioUnit_7_0_Front; | ||||
if (set == AudioChannelSet::createFront7point1()) return kAudioChannelLayoutTag_AudioUnit_7_1_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()); | return static_cast<AudioChannelLayoutTag> ((int) kAudioChannelLayoutTag_DiscreteInOrder | set.size()); | ||||
} | } | ||||
@@ -2048,6 +2158,31 @@ private: | |||||
JUCE_DECLARE_NON_COPYABLE (JuceAU) | 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 | #if BUILD_AU_CARBON_UI | ||||
@@ -64,8 +64,6 @@ | |||||
#define PLUGIN_SDK_DIRECTMIDI 1 | #define PLUGIN_SDK_DIRECTMIDI 1 | ||||
#define DIGI_PASCAL | #define DIGI_PASCAL | ||||
#define Point CarbonDummyPointName | |||||
#define Component CarbonDummyCompName | |||||
#include <MacAlwaysInclude.h> | #include <MacAlwaysInclude.h> | ||||
#endif | #endif | ||||
@@ -86,9 +86,6 @@ | |||||
#include <FicProcessTokens.h> | #include <FicProcessTokens.h> | ||||
#include <ExternalVersionDefines.h> | #include <ExternalVersionDefines.h> | ||||
#undef Point | |||||
#undef Component | |||||
//============================================================================== | //============================================================================== | ||||
#ifdef _MSC_VER | #ifdef _MSC_VER | ||||
#pragma pack (push, 8) | #pragma pack (push, 8) | ||||
@@ -122,19 +119,18 @@ | |||||
#pragma comment(lib, PT_LIB_PATH "RTASClientLib.lib") | #pragma comment(lib, PT_LIB_PATH "RTASClientLib.lib") | ||||
#endif | #endif | ||||
#undef Component | |||||
#undef MemoryBlock | #undef MemoryBlock | ||||
//============================================================================== | //============================================================================== | ||||
#if JUCE_WINDOWS | #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 | #if ! JucePlugin_EditorRequiresKeyboardFocus | ||||
extern void JUCE_CALLTYPE passFocusToHostWindow (void* hostWindow); | extern void JUCE_CALLTYPE passFocusToHostWindow (void* hostWindow); | ||||
#endif | #endif | ||||
#else | #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(); | extern void forwardCurrentKeyEventToHostWindow(); | ||||
#endif | #endif | ||||
@@ -240,7 +236,7 @@ public: | |||||
void timerCallback() override | void timerCallback() override | ||||
{ | { | ||||
if (! juce::Component::isMouseButtonDownAnywhere()) | |||||
if (! Component::isMouseButtonDownAnywhere()) | |||||
{ | { | ||||
stopTimer(); | stopTimer(); | ||||
@@ -290,7 +286,7 @@ public: | |||||
private: | private: | ||||
AudioProcessor* const filter; | AudioProcessor* const filter; | ||||
JucePlugInProcess* const process; | JucePlugInProcess* const process; | ||||
ScopedPointer<juce::Component> wrapper; | |||||
ScopedPointer<Component> wrapper; | |||||
ScopedPointer<AudioProcessorEditor> editorComp; | ScopedPointer<AudioProcessorEditor> editorComp; | ||||
void deleteEditorComp() | void deleteEditorComp() | ||||
@@ -301,7 +297,7 @@ public: | |||||
{ | { | ||||
PopupMenu::dismissAllActiveMenus(); | PopupMenu::dismissAllActiveMenus(); | ||||
if (juce::Component* const modalComponent = juce::Component::getCurrentlyModalComponent()) | |||||
if (Component* const modalComponent = Component::getCurrentlyModalComponent()) | |||||
modalComponent->exitModalState (0); | modalComponent->exitModalState (0); | ||||
filter->editorBeingDeleted (editorComp); | filter->editorBeingDeleted (editorComp); | ||||
@@ -315,7 +311,7 @@ public: | |||||
//============================================================================== | //============================================================================== | ||||
// A component to hold the AudioProcessorEditor, and cope with some housekeeping | // A component to hold the AudioProcessorEditor, and cope with some housekeeping | ||||
// chores when it changes or repaints. | // chores when it changes or repaints. | ||||
class EditorCompWrapper : public juce::Component | |||||
class EditorCompWrapper : public Component | |||||
#if ! JUCE_MAC | #if ! JUCE_MAC | ||||
, public FocusChangeListener | , public FocusChangeListener | ||||
#endif | #endif | ||||
@@ -368,14 +364,14 @@ public: | |||||
void resized() override | void resized() override | ||||
{ | { | ||||
if (juce::Component* const ed = getEditor()) | |||||
if (Component* const ed = getEditor()) | |||||
ed->setBounds (getLocalBounds()); | ed->setBounds (getLocalBounds()); | ||||
repaint(); | repaint(); | ||||
} | } | ||||
#if JUCE_WINDOWS | #if JUCE_WINDOWS | ||||
void globalFocusChanged (juce::Component*) override | |||||
void globalFocusChanged (Component*) override | |||||
{ | { | ||||
#if ! JucePlugin_EditorRequiresKeyboardFocus | #if ! JucePlugin_EditorRequiresKeyboardFocus | ||||
if (hasKeyboardFocus (true)) | if (hasKeyboardFocus (true)) | ||||
@@ -384,7 +380,7 @@ public: | |||||
} | } | ||||
#endif | #endif | ||||
void childBoundsChanged (juce::Component* child) override | |||||
void childBoundsChanged (Component* child) override | |||||
{ | { | ||||
setSize (child->getWidth(), child->getHeight()); | setSize (child->getWidth(), child->getHeight()); | ||||
child->setTopLeftPosition (0, 0); | child->setTopLeftPosition (0, 0); | ||||
@@ -413,7 +409,7 @@ public: | |||||
JuceCustomUIView* const owner; | JuceCustomUIView* const owner; | ||||
int titleW, titleH; | int titleW, titleH; | ||||
juce::Component* getEditor() const { return getChildComponent (0); } | |||||
Component* getEditor() const { return getChildComponent (0); } | |||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EditorCompWrapper) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EditorCompWrapper) | ||||
}; | }; | ||||
@@ -666,9 +662,9 @@ public: | |||||
#if JUCE_WINDOWS | #if JUCE_WINDOWS | ||||
Boolean HandleKeystroke (EventRecord* e) override | 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) | switch (e->message & charCodeMask) | ||||
{ | { | ||||
@@ -40,15 +40,27 @@ class StandalonePluginHolder | |||||
public: | public: | ||||
/** Creates an instance of the default plugin. | /** 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) | : settings (settingsToUse, takeOwnershipOfSettings) | ||||
{ | { | ||||
createPlugin(); | createPlugin(); | ||||
setupAudioDevices(); | |||||
setupAudioDevices (preferredDefaultDeviceName, preferredSetupOptions); | |||||
reloadPluginState(); | reloadPluginState(); | ||||
startPlaying(); | startPlaying(); | ||||
} | } | ||||
@@ -189,7 +201,8 @@ public: | |||||
} | } | ||||
} | } | ||||
void reloadAudioDeviceState() | |||||
void reloadAudioDeviceState (const String& preferredDefaultDeviceName, | |||||
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions) | |||||
{ | { | ||||
ScopedPointer<XmlElement> savedState; | ScopedPointer<XmlElement> savedState; | ||||
@@ -199,7 +212,9 @@ public: | |||||
deviceManager.initialise (processor->getTotalNumInputChannels(), | deviceManager.initialise (processor->getTotalNumInputChannels(), | ||||
processor->getTotalNumOutputChannels(), | processor->getTotalNumOutputChannels(), | ||||
savedState, | savedState, | ||||
true); | |||||
true, | |||||
preferredDefaultDeviceName, | |||||
preferredSetupOptions); | |||||
} | } | ||||
//============================================================================== | //============================================================================== | ||||
@@ -232,19 +247,20 @@ public: | |||||
AudioProcessorPlayer player; | AudioProcessorPlayer player; | ||||
private: | private: | ||||
void setupAudioDevices() | |||||
void setupAudioDevices (const String& preferredDefaultDeviceName, | |||||
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions) | |||||
{ | { | ||||
deviceManager.addAudioCallback (&player); | deviceManager.addAudioCallback (&player); | ||||
deviceManager.addMidiInputCallback (String::empty, &player); | |||||
deviceManager.addMidiInputCallback (String(), &player); | |||||
reloadAudioDeviceState(); | |||||
reloadAudioDeviceState (preferredDefaultDeviceName, preferredSetupOptions); | |||||
} | } | ||||
void shutDownAudioDevices() | void shutDownAudioDevices() | ||||
{ | { | ||||
saveAudioDeviceState(); | saveAudioDeviceState(); | ||||
deviceManager.removeMidiInputCallback (String::empty, &player); | |||||
deviceManager.removeMidiInputCallback (String(), &player); | |||||
deviceManager.removeAudioCallback (&player); | deviceManager.removeAudioCallback (&player); | ||||
} | } | ||||
@@ -273,7 +289,9 @@ public: | |||||
StandaloneFilterWindow (const String& title, | StandaloneFilterWindow (const String& title, | ||||
Colour backgroundColour, | Colour backgroundColour, | ||||
PropertySet* settingsToUse, | PropertySet* settingsToUse, | ||||
bool takeOwnershipOfSettings) | |||||
bool takeOwnershipOfSettings, | |||||
const String& preferredDefaultDeviceName = String(), | |||||
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions = nullptr) | |||||
: DocumentWindow (title, backgroundColour, DocumentWindow::minimiseButton | DocumentWindow::closeButton), | : DocumentWindow (title, backgroundColour, DocumentWindow::minimiseButton | DocumentWindow::closeButton), | ||||
optionsButton ("options") | optionsButton ("options") | ||||
{ | { | ||||
@@ -283,7 +301,8 @@ public: | |||||
optionsButton.addListener (this); | optionsButton.addListener (this); | ||||
optionsButton.setTriggeredOnMouseDown (true); | optionsButton.setTriggeredOnMouseDown (true); | ||||
pluginHolder = new StandalonePluginHolder (settingsToUse, takeOwnershipOfSettings); | |||||
pluginHolder = new StandalonePluginHolder (settingsToUse, takeOwnershipOfSettings, | |||||
preferredDefaultDeviceName, preferredSetupOptions); | |||||
createEditorComp(); | createEditorComp(); | ||||
@@ -110,7 +110,6 @@ | |||||
class JuceVSTWrapper; | class JuceVSTWrapper; | ||||
static bool recursionCheck = false; | static bool recursionCheck = false; | ||||
static juce::uint32 lastMasterIdleCall = 0; | |||||
namespace juce | namespace juce | ||||
{ | { | ||||
@@ -277,14 +276,43 @@ public: | |||||
#endif | #endif | ||||
hostWindow (0) | hostWindow (0) | ||||
{ | { | ||||
int maxNumInChannels, maxNumOutChannels; | |||||
busUtils.findAllCompatibleLayouts(); | busUtils.findAllCompatibleLayouts(); | ||||
// VST-2 does not support disabling buses: so always enable all of them | // VST-2 does not support disabling buses: so always enable all of them | ||||
if (busUtils.hasDynamicInBuses() || busUtils.hasDynamicOutBuses()) | if (busUtils.hasDynamicInBuses() || busUtils.hasDynamicOutBuses()) | ||||
busUtils.enableAllBuses(); | 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->setRateAndBufferSizeDetails (0, 0); | ||||
filter->setPlayHead (this); | filter->setPlayHead (this); | ||||
@@ -295,8 +323,8 @@ public: | |||||
setUniqueID ((int) (JucePlugin_VSTUniqueID)); | setUniqueID ((int) (JucePlugin_VSTUniqueID)); | ||||
setNumInputs (totalNumInChannels); | |||||
setNumOutputs (totalNumOutChannels); | |||||
setNumInputs (maxNumInChannels); | |||||
setNumOutputs (maxNumOutChannels); | |||||
canProcessReplacing (true); | canProcessReplacing (true); | ||||
canDoubleReplacing (filter->supportsDoublePrecisionProcessing()); | canDoubleReplacing (filter->supportsDoublePrecisionProcessing()); | ||||
@@ -544,7 +572,8 @@ public: | |||||
tmpBuffers.channels[i] = inputs[i]; | 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) | if (isBypassed) | ||||
filter->processBlockBypassed (chans, midiEvents); | filter->processBlockBypassed (chans, midiEvents); | ||||
@@ -641,31 +670,20 @@ public: | |||||
{ | { | ||||
isProcessing = true; | 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(); | const int currentBlockSize = getBlockSize(); | ||||
jassert (currentBlockSize > 0); | |||||
firstProcessCallback = true; | firstProcessCallback = true; | ||||
filter->setNonRealtime (getCurrentProcessLevel() == 4 /* kVstProcessLevelOffline */); | filter->setNonRealtime (getCurrentProcessLevel() == 4 /* kVstProcessLevelOffline */); | ||||
filter->setRateAndBufferSizeDetails (rate, currentBlockSize); | |||||
filter->setRateAndBufferSizeDetails (currentRate, currentBlockSize); | |||||
deleteTempChannels(); | deleteTempChannels(); | ||||
filter->prepareToPlay (rate, currentBlockSize); | |||||
filter->prepareToPlay (currentRate, currentBlockSize); | |||||
midiEvents.ensureSize (2048); | midiEvents.ensureSize (2048); | ||||
midiEvents.clear(); | midiEvents.clear(); | ||||
@@ -901,10 +919,6 @@ public: | |||||
bool setSpeakerArrangement (VstSpeakerArrangement* pluginInput, | bool setSpeakerArrangement (VstSpeakerArrangement* pluginInput, | ||||
VstSpeakerArrangement* pluginOutput) override | 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) | if (pluginInput != nullptr && filter->busArrangement.inputBuses.size() == 0) | ||||
return false; | return false; | ||||
@@ -913,34 +927,64 @@ public: | |||||
PluginBusUtilities::ScopedBusRestorer busRestorer (busUtils); | 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 (busUtils.getChannelSet (true, 0) != newType) | ||||
if (! filter->setPreferredBusArrangement (true, 0, newType)) | if (! filter->setPreferredBusArrangement (true, 0, newType)) | ||||
return false; | 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 (busUtils.getChannelSet (false, 0) != newType) | ||||
if (! filter->setPreferredBusArrangement (false, 0, newType)) | if (! filter->setPreferredBusArrangement (false, 0, newType)) | ||||
return false; | return false; | ||||
// did this change the input layout? | |||||
if (oldInputLayout != busUtils.getChannelSet (true, 0) && pluginInput != nullptr) | |||||
return false; | |||||
} | } | ||||
busRestorer.release(); | busRestorer.release(); | ||||
const int totalNumInChannels = busUtils.findTotalNumChannels(true); | |||||
const int totalNumOutChannels = busUtils.findTotalNumChannels(false); | |||||
filter->setRateAndBufferSizeDetails(0, 0); | filter->setRateAndBufferSizeDetails(0, 0); | ||||
setNumInputs (totalNumInChannels); | |||||
setNumOutputs(totalNumOutChannels); | |||||
ioChanged(); | |||||
return true; | return true; | ||||
} | } | ||||
@@ -950,22 +994,35 @@ public: | |||||
*pluginInput = 0; | *pluginInput = 0; | ||||
*pluginOutput = 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; | 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); | AudioEffectX::deallocateArrangement (pluginInput); | ||||
*pluginInput = 0; | *pluginInput = 0; | ||||
return false; | 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; | return true; | ||||
} | } | ||||
@@ -985,11 +1042,14 @@ public: | |||||
bool getPinProperties (VstPinProperties& properties, bool direction, int index) const | bool getPinProperties (VstPinProperties& properties, bool direction, int index) const | ||||
{ | { | ||||
// index refers to the absolute index when combining all channels of every bus | // 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); | const int n = busUtils.getBusCount(direction); | ||||
int busIdx; | int busIdx; | ||||
for (busIdx = 0; busIdx < n; ++busIdx) | for (busIdx = 0; busIdx < n; ++busIdx) | ||||
{ | { | ||||
const int numChans = busUtils.getNumChannels(direction, busIdx); | |||||
const int numChans = busUtils.getNumChannels (direction, busIdx); | |||||
if (index < numChans) | if (index < numChans) | ||||
break; | break; | ||||
@@ -997,25 +1057,30 @@ public: | |||||
} | } | ||||
if (busIdx >= n) | 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); | 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) | if (busInfo.channels.size() == 2) | ||||
properties.flags |= kVstPinIsStereo; | properties.flags |= kVstPinIsStereo; | ||||
@@ -1070,11 +1135,14 @@ public: | |||||
{ | { | ||||
Array<AudioChannelSet::ChannelType> chans (channels.getChannelTypes()); | Array<AudioChannelSet::ChannelType> chans (channels.getChannelTypes()); | ||||
if (channels == AudioChannelSet::disabled()) | |||||
return kSpeakerArrEmpty; | |||||
for (const Mapping* m = getMappings(); m->vst2 != kSpeakerArrEmpty; ++m) | for (const Mapping* m = getMappings(); m->vst2 != kSpeakerArrEmpty; ++m) | ||||
if (m->matches (chans)) | if (m->matches (chans)) | ||||
return m->vst2; | return m->vst2; | ||||
return kSpeakerArrEmpty; | |||||
return kSpeakerArrUserDefined; | |||||
} | } | ||||
static void channelSetToVstArrangement (const AudioChannelSet& channels, VstSpeakerArrangement& result) | static void channelSetToVstArrangement (const AudioChannelSet& channels, VstSpeakerArrangement& result) | ||||
@@ -1134,25 +1202,25 @@ public: | |||||
{ | { | ||||
switch (type) | 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; | default: break; | ||||
} | } | ||||
@@ -1163,29 +1231,29 @@ public: | |||||
{ | { | ||||
switch (type) | 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; | default: break; | ||||
} | } | ||||
return AudioChannelSet::ChannelType::unknown; | |||||
return AudioChannelSet::unknown; | |||||
} | } | ||||
}; | }; | ||||
@@ -1249,25 +1317,6 @@ public: | |||||
if (hostWindow != 0) | if (hostWindow != 0) | ||||
checkWindowVisibilityVST (hostWindow, editorComp, useNSView); | checkWindowVisibilityVST (hostWindow, editorComp, useNSView); | ||||
#endif | #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() | void doIdleCallback() | ||||
@@ -1276,7 +1325,7 @@ public: | |||||
if (MessageManager::getInstance()->isThisTheMessageThread() | if (MessageManager::getInstance()->isThisTheMessageThread() | ||||
&& ! recursionCheck) | && ! recursionCheck) | ||||
{ | { | ||||
recursionCheck = true; | |||||
ScopedValueSetter<bool> svs (recursionCheck, true, false); | |||||
JUCE_AUTORELEASEPOOL | JUCE_AUTORELEASEPOOL | ||||
{ | { | ||||
@@ -1285,8 +1334,6 @@ public: | |||||
for (int i = ComponentPeer::getNumPeers(); --i >= 0;) | for (int i = ComponentPeer::getNumPeers(); --i >= 0;) | ||||
if (ComponentPeer* p = ComponentPeer::getPeer(i)) | if (ComponentPeer* p = ComponentPeer::getPeer(i)) | ||||
p->performAnyPendingRepaintsNow(); | p->performAnyPendingRepaintsNow(); | ||||
recursionCheck = false; | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -1322,7 +1369,7 @@ public: | |||||
PopupMenu::dismissAllActiveMenus(); | PopupMenu::dismissAllActiveMenus(); | ||||
jassert (! recursionCheck); | jassert (! recursionCheck); | ||||
recursionCheck = true; | |||||
ScopedValueSetter<bool> svs (recursionCheck, true, false); | |||||
if (editorComp != nullptr) | if (editorComp != nullptr) | ||||
{ | { | ||||
@@ -1333,7 +1380,6 @@ public: | |||||
if (canDeleteLaterIfModal) | if (canDeleteLaterIfModal) | ||||
{ | { | ||||
shouldDeleteEditor = true; | shouldDeleteEditor = true; | ||||
recursionCheck = false; | |||||
return; | return; | ||||
} | } | ||||
} | } | ||||
@@ -1358,8 +1404,6 @@ public: | |||||
#if JUCE_LINUX | #if JUCE_LINUX | ||||
hostWindow = 0; | hostWindow = 0; | ||||
#endif | #endif | ||||
recursionCheck = false; | |||||
} | } | ||||
} | } | ||||
@@ -1422,7 +1466,7 @@ public: | |||||
{ | { | ||||
editorSize.left = 0; | editorSize.left = 0; | ||||
editorSize.top = 0; | editorSize.top = 0; | ||||
editorSize.right = (VstInt16) editorComp->getWidth(); | |||||
editorSize.right = (VstInt16) editorComp->getWidth(); | |||||
editorSize.bottom = (VstInt16) editorComp->getHeight(); | editorSize.bottom = (VstInt16) editorComp->getHeight(); | ||||
*((ERect**) ptr) = &editorSize; | *((ERect**) ptr) = &editorSize; | ||||
@@ -1515,8 +1559,7 @@ public: | |||||
//============================================================================== | //============================================================================== | ||||
// A component to hold the AudioProcessorEditor, and cope with some housekeeping | // A component to hold the AudioProcessorEditor, and cope with some housekeeping | ||||
// chores when it changes or repaints. | // chores when it changes or repaints. | ||||
class EditorCompWrapper : public Component, | |||||
public AsyncUpdater | |||||
class EditorCompWrapper : public Component | |||||
{ | { | ||||
public: | public: | ||||
EditorCompWrapper (JuceVSTWrapper& w, AudioProcessorEditor* editor) | EditorCompWrapper (JuceVSTWrapper& w, AudioProcessorEditor* editor) | ||||
@@ -1533,6 +1576,8 @@ public: | |||||
if (! getHostType().isReceptor()) | if (! getHostType().isReceptor()) | ||||
addMouseListener (this, true); | addMouseListener (this, true); | ||||
#endif | #endif | ||||
ignoreUnused (fakeMouseGenerator); | |||||
} | } | ||||
~EditorCompWrapper() | ~EditorCompWrapper() | ||||
@@ -1543,15 +1588,6 @@ public: | |||||
void paint (Graphics&) override {} | 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 | #if JUCE_MAC | ||||
bool keyPressed (const KeyPress&) override | bool keyPressed (const KeyPress&) override | ||||
{ | { | ||||
@@ -1605,11 +1641,6 @@ public: | |||||
} | } | ||||
} | } | ||||
void handleAsyncUpdate() override | |||||
{ | |||||
wrapper.tryMasterIdle(); | |||||
} | |||||
#if JUCE_WINDOWS | #if JUCE_WINDOWS | ||||
void mouseDown (const MouseEvent&) override | void mouseDown (const MouseEvent&) override | ||||
{ | { | ||||
@@ -1710,7 +1741,7 @@ private: | |||||
if (filter != nullptr) | if (filter != nullptr) | ||||
{ | { | ||||
int numChannels = filter->getTotalNumInputChannels() + filter->getTotalNumOutputChannels(); | |||||
int numChannels = cEffect.numInputs + cEffect.numOutputs; | |||||
tmpBuffers.tempChannels.insertMultiple (0, nullptr, numChannels); | tmpBuffers.tempChannels.insertMultiple (0, nullptr, numChannels); | ||||
} | } | ||||
} | } | ||||
@@ -1721,6 +1752,29 @@ private: | |||||
deleteTempChannels (doubleTempBuffers); | 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) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceVSTWrapper) | ||||
}; | }; | ||||
@@ -34,9 +34,6 @@ | |||||
#include "../utility/juce_FakeMouseMoveGenerator.h" | #include "../utility/juce_FakeMouseMoveGenerator.h" | ||||
#include "../utility/juce_CarbonVisibility.h" | #include "../utility/juce_CarbonVisibility.h" | ||||
#undef Component | |||||
#undef Point | |||||
//============================================================================== | //============================================================================== | ||||
namespace juce | namespace juce | ||||
{ | { | ||||
@@ -66,6 +63,11 @@ static pascal OSStatus viewBoundsChangedEvent (EventHandlerCallRef, EventRef, vo | |||||
updateEditorCompBoundsVST ((Component*) user); | updateEditorCompBoundsVST ((Component*) user); | ||||
return noErr; | return noErr; | ||||
} | } | ||||
static bool shouldManuallyCloseHostWindow() | |||||
{ | |||||
return getHostType().isCubase7orLater() || getHostType().isRenoise(); | |||||
} | |||||
#endif | #endif | ||||
//============================================================================== | //============================================================================== | ||||
@@ -87,7 +89,7 @@ void* attachComponentToWindowRefVST (Component* comp, void* parentWindowOrView, | |||||
{ | { | ||||
NSWindow* hostWindow = [[NSWindow alloc] initWithWindowRef: parentWindowOrView]; | NSWindow* hostWindow = [[NSWindow alloc] initWithWindowRef: parentWindowOrView]; | ||||
if (getHostType().isCubase7orLater()) | |||||
if (shouldManuallyCloseHostWindow()) | |||||
{ | { | ||||
[hostWindow setReleasedWhenClosed: NO]; | [hostWindow setReleasedWhenClosed: NO]; | ||||
} | } | ||||
@@ -212,7 +214,7 @@ void detachComponentFromWindowRefVST (Component* comp, void* window, bool isNSVi | |||||
comp->removeFromDesktop(); | comp->removeFromDesktop(); | ||||
[pluginView release]; | [pluginView release]; | ||||
if (getHostType().isCubase7orLater()) | |||||
if (shouldManuallyCloseHostWindow()) | |||||
[hostWindow close]; | [hostWindow close]; | ||||
else | else | ||||
[hostWindow release]; | [hostWindow release]; | ||||
@@ -38,6 +38,10 @@ | |||||
#define JUCE_VST3_CAN_REPLACE_VST2 1 | #define JUCE_VST3_CAN_REPLACE_VST2 1 | ||||
#endif | #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_VST3_CAN_REPLACE_VST2 | ||||
#if JUCE_MSVC | #if JUCE_MSVC | ||||
#pragma warning (push) | #pragma warning (push) | ||||
@@ -51,9 +55,6 @@ | |||||
#endif | #endif | ||||
#endif | #endif | ||||
#undef Point | |||||
#undef Component | |||||
namespace juce | namespace juce | ||||
{ | { | ||||
@@ -480,9 +481,11 @@ private: | |||||
parameters.addParameter (new BypassParam (*pluginInstance, numParameters)); | 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); | initialiseMidiControllerMappings (pluginInstance->getNumParameters() + 1); | ||||
#endif | |||||
audioProcessorChanged (pluginInstance); | audioProcessorChanged (pluginInstance); | ||||
} | } | ||||
} | } | ||||
@@ -639,7 +642,7 @@ private: | |||||
private: | private: | ||||
//============================================================================== | //============================================================================== | ||||
class ContentWrapperComponent : public juce::Component | |||||
class ContentWrapperComponent : public Component | |||||
{ | { | ||||
public: | public: | ||||
ContentWrapperComponent (JuceVST3Editor& editor, AudioProcessor& plugin) | ContentWrapperComponent (JuceVST3Editor& editor, AudioProcessor& plugin) | ||||
@@ -1569,11 +1572,13 @@ public: | |||||
const int id = (int) paramQueue->getParameterId(); | const int id = (int) paramQueue->getParameterId(); | ||||
if (isPositiveAndBelow (id, pluginInstance->getNumParameters())) | if (isPositiveAndBelow (id, pluginInstance->getNumParameters())) | ||||
pluginInstance->setParameter (id, (float) value); | |||||
pluginInstance->setParameter (id, static_cast<float> (value)); | |||||
else if (id == vstBypassParameterId) | else if (id == vstBypassParameterId) | ||||
setBypassed (static_cast<float> (value) != 0.0f); | setBypassed (static_cast<float> (value) != 0.0f); | ||||
#if JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS | |||||
else | else | ||||
addParameterChangeToMidiBuffer (offsetSamples, id, value); | addParameterChangeToMidiBuffer (offsetSamples, id, value); | ||||
#endif | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -28,7 +28,6 @@ | |||||
#include "../juce_gui_basics/juce_gui_basics.h" | #include "../juce_gui_basics/juce_gui_basics.h" | ||||
#include "../juce_audio_basics/juce_audio_basics.h" | #include "../juce_audio_basics/juce_audio_basics.h" | ||||
#include "../juce_audio_processors/juce_audio_processors.h" | #include "../juce_audio_processors/juce_audio_processors.h" | ||||
#include "utility/juce_CheckSettingMacros.h" | |||||
namespace juce | namespace juce | ||||
{ | { | ||||
@@ -31,6 +31,11 @@ | |||||
#error "You need to enable at least one plugin format!" | #error "You need to enable at least one plugin format!" | ||||
#endif | #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 | #ifndef JucePlugin_IsSynth | ||||
#error "You need to define the JucePlugin_IsSynth value!" | #error "You need to define the JucePlugin_IsSynth value!" | ||||
#endif | #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" | #error "JucePlugin_Latency is now deprecated - instead, call the AudioProcessor::setLatencySamples() method if your plugin has a non-zero delay" | ||||
#endif | #endif | ||||
#ifndef JucePlugin_SilenceInProducesSilenceOut | |||||
#error "You need to define the JucePlugin_SilenceInProducesSilenceOut value!" | |||||
#endif | |||||
#ifndef JucePlugin_EditorRequiresKeyboardFocus | #ifndef JucePlugin_EditorRequiresKeyboardFocus | ||||
#error "You need to define the JucePlugin_EditorRequiresKeyboardFocus value!" | #error "You need to define the JucePlugin_EditorRequiresKeyboardFocus value!" | ||||
#endif | #endif | ||||
@@ -31,12 +31,12 @@ using namespace juce; | |||||
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() | inline const PluginHostType& getHostType() | ||||
@@ -50,23 +50,14 @@ | |||||
#define JUCE_SUPPORT_CARBON 1 | #define JUCE_SUPPORT_CARBON 1 | ||||
#endif | #endif | ||||
#define Point CarbonDummyPointName | |||||
#if JUCE_SUPPORT_CARBON | |||||
#define Component CarbonDummyCompName | |||||
#endif | |||||
#ifdef __OBJC__ | #ifdef __OBJC__ | ||||
#include <Cocoa/Cocoa.h> | #include <Cocoa/Cocoa.h> | ||||
#endif | #endif | ||||
#if JUCE_SUPPORT_CARBON | #if JUCE_SUPPORT_CARBON | ||||
#include <Carbon/Carbon.h> | #include <Carbon/Carbon.h> | ||||
#undef Component | |||||
#endif | #endif | ||||
#undef Point | |||||
#include <objc/runtime.h> | #include <objc/runtime.h> | ||||
#include <objc/objc.h> | #include <objc/objc.h> | ||||
#include <objc/message.h> | #include <objc/message.h> | ||||
@@ -72,6 +72,15 @@ struct PluginBusUtilities | |||||
return nullptr; | 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; | int defaultLayoutIndex; | ||||
bool busIgnoresLayout, canBeDisabled, isEnabledByDefault; | bool busIgnoresLayout, canBeDisabled, isEnabledByDefault; | ||||
SortedSet<AudioChannelSet> supportedLayouts; | SortedSet<AudioChannelSet> supportedLayouts; | ||||
@@ -88,12 +97,12 @@ struct PluginBusUtilities | |||||
bool hasOutputs (int bus) const noexcept { return isBusEnabled (false, bus); } | 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 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; | int total = 0; | ||||
const AudioBusArray& ioBuses = getFilterBus (isInput); | 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(); | total += ioBuses.getReference (i).channels.size(); | ||||
return total; | return total; | ||||
@@ -341,7 +350,11 @@ private: | |||||
case 2: | case 2: | ||||
sets.add (AudioChannelSet::stereo()); | sets.add (AudioChannelSet::stereo()); | ||||
break; | break; | ||||
case 3: | |||||
sets.add (AudioChannelSet::createLCR()); | |||||
break; | |||||
case 4: | case 4: | ||||
sets.add (AudioChannelSet::createLCRS()); | |||||
sets.add (AudioChannelSet::quadraphonic()); | sets.add (AudioChannelSet::quadraphonic()); | ||||
sets.add (AudioChannelSet::ambisonic()); | sets.add (AudioChannelSet::ambisonic()); | ||||
break; | break; | ||||
@@ -351,6 +364,7 @@ private: | |||||
break; | break; | ||||
case 6: | case 6: | ||||
sets.add (AudioChannelSet::hexagonal()); | sets.add (AudioChannelSet::hexagonal()); | ||||
sets.add (AudioChannelSet::create5point1()); | |||||
sets.add (AudioChannelSet::create6point0()); | sets.add (AudioChannelSet::create6point0()); | ||||
break; | break; | ||||
case 7: | case 7: | ||||
@@ -53,6 +53,7 @@ public: | |||||
MergingPyramix, | MergingPyramix, | ||||
MuseReceptorGeneric, | MuseReceptorGeneric, | ||||
Reaper, | Reaper, | ||||
Renoise, | |||||
SteinbergCubase4, | SteinbergCubase4, | ||||
SteinbergCubase5, | SteinbergCubase5, | ||||
SteinbergCubase5Bridged, | SteinbergCubase5Bridged, | ||||
@@ -80,31 +81,33 @@ public: | |||||
HostType type; | 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 | const char* getHostDescription() const noexcept | ||||
@@ -128,6 +131,7 @@ public: | |||||
case MergingPyramix: return "Pyramix"; | case MergingPyramix: return "Pyramix"; | ||||
case MuseReceptorGeneric: return "Muse Receptor"; | case MuseReceptorGeneric: return "Muse Receptor"; | ||||
case Reaper: return "Reaper"; | case Reaper: return "Reaper"; | ||||
case Renoise: return "Renoise"; | |||||
case SteinbergCubase4: return "Steinberg Cubase 4"; | case SteinbergCubase4: return "Steinberg Cubase 4"; | ||||
case SteinbergCubase5: return "Steinberg Cubase 5"; | case SteinbergCubase5: return "Steinberg Cubase 5"; | ||||
case SteinbergCubase5Bridged: return "Steinberg Cubase 5 Bridged"; | case SteinbergCubase5Bridged: return "Steinberg Cubase 5 Bridged"; | ||||
@@ -198,6 +202,7 @@ private: | |||||
if (hostPath.containsIgnoreCase ("Studio One")) return StudioOne; | if (hostPath.containsIgnoreCase ("Studio One")) return StudioOne; | ||||
if (hostPath.containsIgnoreCase ("Tracktion 3")) return Tracktion3; | if (hostPath.containsIgnoreCase ("Tracktion 3")) return Tracktion3; | ||||
if (hostFilename.containsIgnoreCase ("Tracktion")) return TracktionGeneric; | if (hostFilename.containsIgnoreCase ("Tracktion")) return TracktionGeneric; | ||||
if (hostFilename.containsIgnoreCase ("Renoise")) return Renoise; | |||||
#elif JUCE_WINDOWS | #elif JUCE_WINDOWS | ||||
if (hostFilename.containsIgnoreCase ("Live 6.")) return AbletonLive6; | if (hostFilename.containsIgnoreCase ("Live 6.")) return AbletonLive6; | ||||
@@ -235,6 +240,7 @@ private: | |||||
if (hostFilename.containsIgnoreCase ("VST_Scanner")) return VBVSTScanner; | if (hostFilename.containsIgnoreCase ("VST_Scanner")) return VBVSTScanner; | ||||
if (hostPath.containsIgnoreCase ("Merging Technologies")) return MergingPyramix; | if (hostPath.containsIgnoreCase ("Merging Technologies")) return MergingPyramix; | ||||
if (hostFilename.startsWithIgnoreCase ("Sam")) return MagixSamplitude; | if (hostFilename.startsWithIgnoreCase ("Sam")) return MagixSamplitude; | ||||
if (hostFilename.containsIgnoreCase ("Renoise")) return Renoise; | |||||
#elif JUCE_LINUX | #elif JUCE_LINUX | ||||
if (hostFilename.containsIgnoreCase ("Ardour")) return Ardour; | if (hostFilename.containsIgnoreCase ("Ardour")) return Ardour; | ||||
@@ -27,7 +27,6 @@ | |||||
#endif | #endif | ||||
#include "../../juce_core/system/juce_TargetPlatform.h" | #include "../../juce_core/system/juce_TargetPlatform.h" | ||||
#include "../utility/juce_CheckSettingMacros.h" | |||||
#include "juce_IncludeModuleHeaders.h" | #include "juce_IncludeModuleHeaders.h" | ||||
#if _MSC_VER || JUCE_MINGW | #if _MSC_VER || JUCE_MINGW | ||||
@@ -387,11 +387,6 @@ public: | |||||
void* getPlatformSpecificData() override { return audioUnit; } | void* getPlatformSpecificData() override { return audioUnit; } | ||||
const String getName() const override { return pluginName; } | const String getName() const override { return pluginName; } | ||||
bool silenceInProducesSilenceOut() const override | |||||
{ | |||||
return getTailLengthSeconds() <= 0; | |||||
} | |||||
double getTailLengthSeconds() const override | double getTailLengthSeconds() const override | ||||
{ | { | ||||
Float64 tail = 0; | Float64 tail = 0; | ||||
@@ -252,7 +252,6 @@ public: | |||||
bool acceptsMidi() const { return false; } | bool acceptsMidi() const { return false; } | ||||
bool producesMidi() 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; } | double getTailLengthSeconds() const { return 0.0; } | ||||
//============================================================================== | //============================================================================== | ||||
@@ -143,25 +143,25 @@ static inline Steinberg::Vst::Speaker getSpeakerType (AudioChannelSet::ChannelTy | |||||
switch (type) | 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; | default: break; | ||||
} | } | ||||
@@ -174,29 +174,29 @@ static inline AudioChannelSet::ChannelType getChannelType (Steinberg::Vst::Speak | |||||
switch (type) | 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; | default: break; | ||||
} | } | ||||
return AudioChannelSet::ChannelType::unknown; | |||||
return AudioChannelSet::unknown; | |||||
} | } | ||||
static inline Steinberg::Vst::SpeakerArrangement getSpeakerArrangement (const AudioChannelSet& channels) noexcept | static inline Steinberg::Vst::SpeakerArrangement getSpeakerArrangement (const AudioChannelSet& channels) noexcept | ||||
@@ -25,9 +25,6 @@ | |||||
#ifndef JUCE_VST3HEADERS_H_INCLUDED | #ifndef JUCE_VST3HEADERS_H_INCLUDED | ||||
#define 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. | // Wow, those Steinberg guys really don't worry too much about compiler warnings. | ||||
#if _MSC_VER | #if _MSC_VER | ||||
#pragma warning (disable: 4505) | #pragma warning (disable: 4505) | ||||
@@ -85,8 +82,6 @@ | |||||
#if JUCE_MINGW | #if JUCE_MINGW | ||||
#define _set_abort_behavior(...) | #define _set_abort_behavior(...) | ||||
#endif | #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/baseiids.cpp> | ||||
#include <base/source/fatomic.cpp> | #include <base/source/fatomic.cpp> | ||||
#include <base/source/fbuffer.cpp> | #include <base/source/fbuffer.cpp> | ||||
@@ -112,7 +107,6 @@ | |||||
#include <public.sdk/source/vst/vstcomponentbase.cpp> | #include <public.sdk/source/vst/vstcomponentbase.cpp> | ||||
#include <public.sdk/source/vst/vstparameters.cpp> | #include <public.sdk/source/vst/vstparameters.cpp> | ||||
#include <public.sdk/source/vst/hosting/hostclasses.cpp> | #include <public.sdk/source/vst/hosting/hostclasses.cpp> | ||||
#undef Point | |||||
//============================================================================== | //============================================================================== | ||||
namespace Steinberg | namespace Steinberg | ||||
@@ -177,7 +171,5 @@ namespace Steinberg | |||||
#undef DEF_CLASS2 | #undef DEF_CLASS2 | ||||
#undef DEF_CLASS_W | #undef DEF_CLASS_W | ||||
#undef END_FACTORY | #undef END_FACTORY | ||||
#undef Point | |||||
#undef Component | |||||
#endif // JUCE_VST3HEADERS_H_INCLUDED | #endif // JUCE_VST3HEADERS_H_INCLUDED |
@@ -1861,23 +1861,15 @@ public: | |||||
bool producesMidi() const override { return getBusInfo (false, false).channelCount > 0; } | 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." */ | /** 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 | double getTailLengthSeconds() const override | ||||
{ | { | ||||
if (processor != nullptr) | 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; | return 0.0; | ||||
@@ -890,23 +890,18 @@ public: | |||||
return uid; | return uid; | ||||
} | } | ||||
bool silenceInProducesSilenceOut() const override | |||||
{ | |||||
return effect == nullptr || (effect->flags & effFlagsNoSoundInStop) != 0; | |||||
} | |||||
double getTailLengthSeconds() const override | double getTailLengthSeconds() const override | ||||
{ | { | ||||
if (effect == nullptr) | if (effect == nullptr) | ||||
return 0.0; | return 0.0; | ||||
const double currentSampleRate = getSampleRate(); | |||||
const double sampleRate = getSampleRate(); | |||||
if (currentSampleRate <= 0) | |||||
if (sampleRate <= 0) | |||||
return 0.0; | return 0.0; | ||||
VstIntPtr samples = dispatch (effGetTailSize, 0, 0, 0, 0); | VstIntPtr samples = dispatch (effGetTailSize, 0, 0, 0, 0); | ||||
return samples / currentSampleRate; | |||||
return samples / sampleRate; | |||||
} | } | ||||
bool acceptsMidi() const override { return wantsMidiMessages; } | bool acceptsMidi() const override { return wantsMidiMessages; } | ||||
@@ -2915,11 +2910,11 @@ FileSearchPath VSTPluginFormat::getDefaultLocationsToSearch() | |||||
const String programFiles (File::getSpecialLocation (File::globalApplicationsDirectory).getFullPathName()); | const String programFiles (File::getSpecialLocation (File::globalApplicationsDirectory).getFullPathName()); | ||||
FileSearchPath paths; | FileSearchPath paths; | ||||
paths.add (WindowsRegistry::getValue ("HKLM\\Software\\VST\\VSTPluginsPath", | |||||
paths.add (WindowsRegistry::getValue ("HKEY_LOCAL_MACHINE\\Software\\VST\\VSTPluginsPath", | |||||
programFiles + "\\Steinberg\\VstPlugins")); | programFiles + "\\Steinberg\\VstPlugins")); | ||||
paths.removeNonExistentPaths(); | paths.removeNonExistentPaths(); | ||||
paths.add (WindowsRegistry::getValue ("HKLM\\Software\\VST\\VSTPluginsPath", | |||||
paths.add (WindowsRegistry::getValue ("HKEY_LOCAL_MACHINE\\Software\\VST\\VSTPluginsPath", | |||||
programFiles + "\\VstPlugins")); | programFiles + "\\VstPlugins")); | ||||
return paths; | return paths; | ||||
#endif | #endif | ||||
@@ -31,7 +31,8 @@ | |||||
#error "Incorrect use of JUCE cpp file" | #error "Incorrect use of JUCE cpp file" | ||||
#endif | #endif | ||||
#include "../juce_core/native/juce_BasicNativeHeaders.h" | |||||
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1 | |||||
#include "juce_audio_processors.h" | #include "juce_audio_processors.h" | ||||
#include "../juce_gui_extra/juce_gui_extra.h" | #include "../juce_gui_extra/juce_gui_extra.h" | ||||
@@ -40,11 +41,7 @@ | |||||
#if JUCE_SUPPORT_CARBON \ | #if JUCE_SUPPORT_CARBON \ | ||||
&& ((JUCE_PLUGINHOST_VST || JUCE_PLUGINHOST_AU) \ | && ((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)) | || ! (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> | #include <Carbon/Carbon.h> | ||||
#undef Point | |||||
#undef Component | |||||
#endif | #endif | ||||
#endif | #endif | ||||
@@ -29,7 +29,7 @@ | |||||
#include "../juce_audio_basics/juce_audio_basics.h" | #include "../juce_audio_basics/juce_audio_basics.h" | ||||
//============================================================================= | |||||
//============================================================================== | |||||
/** Config: JUCE_PLUGINHOST_VST | /** Config: JUCE_PLUGINHOST_VST | ||||
Enables the VST audio plugin hosting classes. This requires the Steinberg VST SDK to be | Enables the VST audio plugin hosting classes. This requires the Steinberg VST SDK to be | ||||
installed on your machine. | installed on your machine. | ||||
@@ -67,8 +67,8 @@ | |||||
#define JUCE_SUPPORT_CARBON 1 | #define JUCE_SUPPORT_CARBON 1 | ||||
#endif | #endif | ||||
//============================================================================= | |||||
//============================================================================= | |||||
//============================================================================== | |||||
//============================================================================== | |||||
namespace juce | 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; } | ||||
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) | switch (type) | ||||
{ | { | ||||
case left: return NEEDS_TRANS("Left"); | case left: return NEEDS_TRANS("Left"); | ||||
@@ -63,8 +66,11 @@ const char* AudioChannelSet::getChannelTypeName (AudioChannelSet::ChannelType ty | |||||
return "Unknown"; | 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) | switch (type) | ||||
{ | { | ||||
case left: return "L"; | case left: return "L"; | ||||
@@ -151,17 +157,17 @@ AudioChannelSet AudioChannelSet::mono() { return AudioChannelSet ( | |||||
AudioChannelSet AudioChannelSet::stereo() { return AudioChannelSet ((1u << left) | (1u << right)); } | AudioChannelSet AudioChannelSet::stereo() { return AudioChannelSet ((1u << left) | (1u << right)); } | ||||
AudioChannelSet AudioChannelSet::createLCR() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre)); } | 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::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::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::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)); } | 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". */ | /** 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". */ | /** 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. */ | /** Adds a channel to the set. */ | ||||
@@ -32,7 +32,7 @@ void JUCE_CALLTYPE AudioProcessor::setTypeOfNextNewPlugin (AudioProcessor::Wrapp | |||||
AudioProcessor::AudioProcessor() | AudioProcessor::AudioProcessor() | ||||
: wrapperType (wrapperTypeBeingCreated.get()), | : wrapperType (wrapperTypeBeingCreated.get()), | ||||
playHead (nullptr), | playHead (nullptr), | ||||
sampleRate (0), | |||||
currentSampleRate (0), | |||||
blockSize (0), | blockSize (0), | ||||
latencySamples (0), | latencySamples (0), | ||||
#if JUCE_DEBUG | #if JUCE_DEBUG | ||||
@@ -42,23 +42,25 @@ AudioProcessor::AudioProcessor() | |||||
nonRealtime (false), | nonRealtime (false), | ||||
processingPrecision (singlePrecision) | processingPrecision (singlePrecision) | ||||
{ | { | ||||
#if ! JucePlugin_IsMidiEffect | |||||
#ifdef JucePlugin_PreferredChannelConfigurations | #ifdef JucePlugin_PreferredChannelConfigurations | ||||
const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations }; | const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations }; | ||||
#else | #else | ||||
const short channelConfigs[][2] = { {2, 2} }; | const short channelConfigs[][2] = { {2, 2} }; | ||||
#endif | #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(); | updateSpeakerFormatStrings(); | ||||
} | } | ||||
@@ -123,7 +125,7 @@ void AudioProcessor::setPlayConfigDetails (const int newNumIns, | |||||
void AudioProcessor::setRateAndBufferSizeDetails (double newSampleRate, int newBlockSize) noexcept | void AudioProcessor::setRateAndBufferSizeDetails (double newSampleRate, int newBlockSize) noexcept | ||||
{ | { | ||||
sampleRate = newSampleRate; | |||||
currentSampleRate = newSampleRate; | |||||
blockSize = newBlockSize; | blockSize = newBlockSize; | ||||
} | } | ||||
@@ -317,6 +317,14 @@ public: | |||||
changing the channel layout of other buses, for example, if your plug-in requires the same | changing the channel layout of other buses, for example, if your plug-in requires the same | ||||
number of input and output channels. | 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 | 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 | be called several hundred times during initialization. If you require any layout specific | ||||
allocations then defer these to prepareToPlay callback. | allocations then defer these to prepareToPlay callback. | ||||
@@ -423,7 +431,7 @@ public: | |||||
This can be called from your processBlock() method - it's not guaranteed | 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. | 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. | /** Returns the current typical block size that is being used. | ||||
@@ -453,9 +461,6 @@ public: | |||||
*/ | */ | ||||
void setLatencySamples (int newLatency); | 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. */ | /** Returns the length of the filter's tail, in seconds. */ | ||||
virtual double getTailLengthSeconds() const = 0; | 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 | /** 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.*/ | and do not care about the layout of channels. Otherwise use setRateAndBufferSizeDetails.*/ | ||||
void setPlayConfigDetails (int numIns, int numOuts, double sampleRate, int blockSize); | void setPlayConfigDetails (int numIns, int numOuts, double sampleRate, int blockSize); | ||||
@@ -919,7 +924,7 @@ public: | |||||
WrapperType wrapperType; | WrapperType wrapperType; | ||||
//============================================================================== | //============================================================================== | ||||
#ifndef DOXYGEN | |||||
#ifndef DOXYGEN | |||||
/** Deprecated: use getTotalNumInputChannels instead. */ | /** Deprecated: use getTotalNumInputChannels instead. */ | ||||
JUCE_DEPRECATED_WITH_BODY (int getNumInputChannels() const noexcept, { return getTotalNumInputChannels(); }) | JUCE_DEPRECATED_WITH_BODY (int getNumInputChannels() const noexcept, { return getTotalNumInputChannels(); }) | ||||
JUCE_DEPRECATED_WITH_BODY (int getNumOutputChannels() const noexcept, { return getTotalNumOutputChannels(); }) | JUCE_DEPRECATED_WITH_BODY (int getNumOutputChannels() const noexcept, { return getTotalNumOutputChannels(); }) | ||||
@@ -946,7 +951,7 @@ public: | |||||
the constructor. */ | the constructor. */ | ||||
JUCE_DEPRECATED (virtual bool isInputChannelStereoPair (int index) const); | JUCE_DEPRECATED (virtual bool isInputChannelStereoPair (int index) const); | ||||
JUCE_DEPRECATED (virtual bool isOutputChannelStereoPair (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. | /** Helper function that just converts an xml element into a binary blob. | ||||
@@ -982,7 +987,7 @@ private: | |||||
#if ! JUCE_AUDIO_PROCESSOR_NO_GUI | #if ! JUCE_AUDIO_PROCESSOR_NO_GUI | ||||
Component::SafePointer<AudioProcessorEditor> activeEditor; | Component::SafePointer<AudioProcessorEditor> activeEditor; | ||||
#endif | #endif | ||||
double sampleRate; | |||||
double currentSampleRate; | |||||
int blockSize, latencySamples; | int blockSize, latencySamples; | ||||
#if JUCE_DEBUG | #if JUCE_DEBUG | ||||
bool textRecursionCheck; | bool textRecursionCheck; | ||||
@@ -1005,6 +1010,9 @@ private: | |||||
void disableNonMainBuses (bool isInput); | void disableNonMainBuses (bool isInput); | ||||
void updateSpeakerFormatStrings(); | 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) | 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); | midiMessages.addEvents (currentMidiOutputBuffer, 0, buffer.getNumSamples(), 0); | ||||
} | } | ||||
bool AudioProcessorGraph::silenceInProducesSilenceOut() const { return false; } | |||||
double AudioProcessorGraph::getTailLengthSeconds() const { return 0; } | double AudioProcessorGraph::getTailLengthSeconds() const { return 0; } | ||||
bool AudioProcessorGraph::acceptsMidi() const { return true; } | bool AudioProcessorGraph::acceptsMidi() const { return true; } | ||||
bool AudioProcessorGraph::producesMidi() const { return true; } | bool AudioProcessorGraph::producesMidi() const { return true; } | ||||
@@ -1601,11 +1600,6 @@ void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioBuffer<doubl | |||||
processAudio (buffer, midiMessages); | processAudio (buffer, midiMessages); | ||||
} | } | ||||
bool AudioProcessorGraph::AudioGraphIOProcessor::silenceInProducesSilenceOut() const | |||||
{ | |||||
return isOutput(); | |||||
} | |||||
double AudioProcessorGraph::AudioGraphIOProcessor::getTailLengthSeconds() const | double AudioProcessorGraph::AudioGraphIOProcessor::getTailLengthSeconds() const | ||||
{ | { | ||||
return 0; | return 0; | ||||
@@ -311,7 +311,6 @@ public: | |||||
void processBlock (AudioBuffer<double>&, MidiBuffer&) override; | void processBlock (AudioBuffer<double>&, MidiBuffer&) override; | ||||
bool supportsDoublePrecisionProcessing() const override; | bool supportsDoublePrecisionProcessing() const override; | ||||
bool silenceInProducesSilenceOut() const override; | |||||
double getTailLengthSeconds() const override; | double getTailLengthSeconds() const override; | ||||
bool acceptsMidi() const override; | bool acceptsMidi() const override; | ||||
bool producesMidi() const override; | bool producesMidi() const override; | ||||
@@ -356,7 +355,6 @@ public: | |||||
void setNonRealtime (bool) noexcept override; | void setNonRealtime (bool) noexcept override; | ||||
void setPlayHead (AudioPlayHead*) override; | void setPlayHead (AudioPlayHead*) override; | ||||
bool silenceInProducesSilenceOut() const override; | |||||
double getTailLengthSeconds() const override; | double getTailLengthSeconds() const override; | ||||
bool acceptsMidi() const override; | bool acceptsMidi() const override; | ||||
bool producesMidi() const override; | bool producesMidi() const override; | ||||
@@ -50,14 +50,17 @@ void AudioParameterFloat::setValue (float newValue) { value | |||||
float AudioParameterFloat::getDefaultValue() const { return range.convertTo0to1 (defaultValue); } | float AudioParameterFloat::getDefaultValue() const { return range.convertTo0to1 (defaultValue); } | ||||
int AudioParameterFloat::getNumSteps() const { return AudioProcessorParameterWithID::getNumSteps(); } | int AudioParameterFloat::getNumSteps() const { return AudioProcessorParameterWithID::getNumSteps(); } | ||||
float AudioParameterFloat::getValueForText (const String& text) const { return range.convertTo0to1 (text.getFloatValue()); } | 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; | return *this; | ||||
} | } | ||||
@@ -87,10 +90,8 @@ String AudioParameterInt::getText (float v, int /*length*/) const { retur | |||||
AudioParameterInt& AudioParameterInt::operator= (int newValue) | AudioParameterInt& AudioParameterInt::operator= (int newValue) | ||||
{ | { | ||||
const float normalisedValue = convertTo0to1 (newValue); | |||||
if (value != normalisedValue) | |||||
setValueNotifyingHost (normalisedValue); | |||||
if (get() != newValue) | |||||
setValueNotifyingHost (convertTo0to1 (newValue)); | |||||
return *this; | return *this; | ||||
} | } | ||||
@@ -115,10 +116,8 @@ String AudioParameterBool::getText (float v, int /*length*/) const { retur | |||||
AudioParameterBool& AudioParameterBool::operator= (bool newValue) | 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; | return *this; | ||||
} | } | ||||
@@ -148,10 +147,8 @@ String AudioParameterChoice::getText (float v, int /*length*/) const { retur | |||||
AudioParameterChoice& AudioParameterChoice::operator= (int newValue) | AudioParameterChoice& AudioParameterChoice::operator= (int newValue) | ||||
{ | { | ||||
const float normalisedValue = convertTo0to1 (newValue); | |||||
if (value != normalisedValue) | |||||
setValueNotifyingHost (normalisedValue); | |||||
if (getIndex() != newValue) | |||||
setValueNotifyingHost (convertTo0to1 (newValue)); | |||||
return *this; | 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(); | updateParameterConnectionsToChildTrees(); | ||||
} | } | ||||
void AudioProcessorValueTreeState::valueTreeChildAdded (ValueTree& parent, ValueTree&) | |||||
void AudioProcessorValueTreeState::valueTreeChildAdded (ValueTree& parent, ValueTree& tree) | |||||
{ | { | ||||
if (parent == state) | |||||
if (parent == state && tree.hasType (valueType)) | |||||
updateParameterConnectionsToChildTrees(); | 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(); | updateParameterConnectionsToChildTrees(); | ||||
} | } | ||||
@@ -112,7 +112,7 @@ public: | |||||
*/ | */ | ||||
void getChannelAsPath (Path& result, const Range<float>* levels, int numLevels, int nextSample); | void getChannelAsPath (Path& result, const Range<float>* levels, int numLevels, int nextSample); | ||||
//========================================================================== | |||||
//============================================================================== | |||||
/** @internal */ | /** @internal */ | ||||
void paint (Graphics&) override; | void paint (Graphics&) override; | ||||
@@ -270,6 +270,13 @@ int MidiKeyboardComponent::getKeyStartPosition (const int midiNoteNumber) const | |||||
return x; | return x; | ||||
} | } | ||||
int MidiKeyboardComponent::getTotalKeyboardWidth() const noexcept | |||||
{ | |||||
int x, w; | |||||
getKeyPos (rangeEnd, x, w); | |||||
return x + w; | |||||
} | |||||
int MidiKeyboardComponent::getNoteAtPosition (Point<int> p) | int MidiKeyboardComponent::getNoteAtPosition (Point<int> p) | ||||
{ | { | ||||
float v; | float v; | ||||
@@ -209,6 +209,9 @@ public: | |||||
*/ | */ | ||||
int getKeyStartPosition (int midiNoteNumber) const; | 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. */ | /** Returns the key at a given coordinate. */ | ||||
int getNoteAtPosition (Point<int> position); | int getNoteAtPosition (Point<int> position); | ||||
@@ -31,7 +31,9 @@ | |||||
#error "Incorrect use of JUCE cpp file" | #error "Incorrect use of JUCE cpp file" | ||||
#endif | #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" | #include "juce_audio_utils.h" | ||||
namespace juce | namespace juce | ||||
@@ -54,7 +56,6 @@ namespace juce | |||||
#elif JUCE_IOS | #elif JUCE_IOS | ||||
#include "native/juce_ios_BluetoothMidiDevicePairingDialogue.mm" | #include "native/juce_ios_BluetoothMidiDevicePairingDialogue.mm" | ||||
#elif JUCE_ANDROID | #elif JUCE_ANDROID | ||||
#include "../juce_core/native/juce_android_JNIHelpers.h" | |||||
#include "native/juce_android_BluetoothMidiDevicePairingDialogue.cpp" | #include "native/juce_android_BluetoothMidiDevicePairingDialogue.cpp" | ||||
#elif JUCE_LINUX | #elif JUCE_LINUX | ||||
#include "native/juce_linux_BluetoothMidiDevicePairingDialogue.cpp" | #include "native/juce_linux_BluetoothMidiDevicePairingDialogue.cpp" | ||||
@@ -30,7 +30,7 @@ | |||||
#include "../juce_audio_formats/juce_audio_formats.h" | #include "../juce_audio_formats/juce_audio_formats.h" | ||||
#include "../juce_audio_processors/juce_audio_processors.h" | #include "../juce_audio_processors/juce_audio_processors.h" | ||||
//============================================================================= | |||||
//============================================================================== | |||||
namespace juce | namespace juce | ||||
{ | { | ||||
@@ -64,7 +64,7 @@ struct AndroidBluetoothMidiInterface | |||||
return retval; | return retval; | ||||
} | } | ||||
//========================================================================== | |||||
//============================================================================== | |||||
static bool pairBluetoothMidiDevice (const String& bluetoothAddress) | static bool pairBluetoothMidiDevice (const String& bluetoothAddress) | ||||
{ | { | ||||
JNIEnv* env = getEnv(); | JNIEnv* env = getEnv(); | ||||
@@ -90,7 +90,7 @@ struct AndroidBluetoothMidiInterface | |||||
javaString (bluetoothAddress).get()); | javaString (bluetoothAddress).get()); | ||||
} | } | ||||
//========================================================================== | |||||
//============================================================================== | |||||
static String getHumanReadableStringForBluetoothAddress (const String& address) | static String getHumanReadableStringForBluetoothAddress (const String& address) | ||||
{ | { | ||||
JNIEnv* env = getEnv(); | JNIEnv* env = getEnv(); | ||||
@@ -111,7 +111,7 @@ struct AndroidBluetoothMidiInterface | |||||
return juceString (string); | return juceString (string); | ||||
} | } | ||||
//========================================================================== | |||||
//============================================================================== | |||||
static bool isBluetoothDevicePaired (const String& address) | static bool isBluetoothDevicePaired (const String& address) | ||||
{ | { | ||||
JNIEnv* env = getEnv(); | JNIEnv* env = getEnv(); | ||||
@@ -166,7 +166,7 @@ class AndroidBluetoothMidiDevicesListBox : public ListBox, | |||||
private Timer | private Timer | ||||
{ | { | ||||
public: | public: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
AndroidBluetoothMidiDevicesListBox() | AndroidBluetoothMidiDevicesListBox() | ||||
: timerPeriodInMs (1000) | : timerPeriodInMs (1000) | ||||
{ | { | ||||
@@ -184,7 +184,7 @@ public: | |||||
} | } | ||||
private: | private: | ||||
//========================================================================== | |||||
//============================================================================== | |||||
typedef AndroidBluetoothMidiDevice::ConnectionStatus DeviceStatus; | typedef AndroidBluetoothMidiDevice::ConnectionStatus DeviceStatus; | ||||
int getNumRows() override | int getNumRows() override | ||||
@@ -226,7 +226,7 @@ private: | |||||
} | } | ||||
} | } | ||||
//========================================================================== | |||||
//============================================================================== | |||||
static Colour getDeviceNameFontColour (DeviceStatus deviceStatus) noexcept | static Colour getDeviceNameFontColour (DeviceStatus deviceStatus) noexcept | ||||
{ | { | ||||
if (deviceStatus == AndroidBluetoothMidiDevice::offline) | if (deviceStatus == AndroidBluetoothMidiDevice::offline) | ||||
@@ -261,7 +261,7 @@ private: | |||||
return "Status unknown"; | return "Status unknown"; | ||||
} | } | ||||
//========================================================================== | |||||
//============================================================================== | |||||
void listBoxItemClicked (int row, const MouseEvent&) override | void listBoxItemClicked (int row, const MouseEvent&) override | ||||
{ | { | ||||
const AndroidBluetoothMidiDevice& device = devices.getReference (row); | const AndroidBluetoothMidiDevice& device = devices.getReference (row); | ||||
@@ -278,7 +278,7 @@ private: | |||||
updateDeviceList(); | updateDeviceList(); | ||||
} | } | ||||
//========================================================================== | |||||
//============================================================================== | |||||
struct PairDeviceThread : public Thread, | struct PairDeviceThread : public Thread, | ||||
private AsyncUpdater | private AsyncUpdater | ||||
{ | { | ||||
@@ -310,7 +310,7 @@ private: | |||||
Component::SafePointer<AndroidBluetoothMidiDevicesListBox> owner; | Component::SafePointer<AndroidBluetoothMidiDevicesListBox> owner; | ||||
}; | }; | ||||
//========================================================================== | |||||
//============================================================================== | |||||
void disconnectedDeviceClicked (int row) | void disconnectedDeviceClicked (int row) | ||||
{ | { | ||||
stopTimer(); | stopTimer(); | ||||
@@ -332,7 +332,7 @@ private: | |||||
AndroidBluetoothMidiInterface::unpairBluetoothMidiDevice (device.bluetoothAddress); | AndroidBluetoothMidiInterface::unpairBluetoothMidiDevice (device.bluetoothAddress); | ||||
} | } | ||||
//========================================================================== | |||||
//============================================================================== | |||||
void updateDeviceList() | void updateDeviceList() | ||||
{ | { | ||||
StringArray bluetoothAddresses = AndroidBluetoothMidiInterface::getBluetoothMidiDevicesNearby(); | StringArray bluetoothAddresses = AndroidBluetoothMidiInterface::getBluetoothMidiDevicesNearby(); | ||||
@@ -427,6 +427,15 @@ private: | |||||
//============================================================================== | //============================================================================== | ||||
bool BluetoothMidiDevicePairingDialogue::open() | 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; | BluetoothMidiSelectorOverlay* overlay = new BluetoothMidiSelectorOverlay; | ||||
return true; | return true; | ||||
} | } | ||||
@@ -424,6 +424,13 @@ public: | |||||
return entry != nullptr ? entry->value : ValueType(); | return entry != nullptr ? entry->value : ValueType(); | ||||
} | } | ||||
/** Resets the iterator to its starting position. */ | |||||
void reset() noexcept | |||||
{ | |||||
entry = nullptr; | |||||
index = 0; | |||||
} | |||||
private: | private: | ||||
//============================================================================== | //============================================================================== | ||||
const HashMap& hashMap; | const HashMap& hashMap; | ||||
@@ -70,8 +70,24 @@ File& File::operator= (File&& other) noexcept | |||||
const File File::nonexistent; | 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) | String File::parseAbsolutePath (const String& p) | ||||
{ | { | ||||
if (p.isEmpty()) | if (p.isEmpty()) | ||||
@@ -81,6 +97,9 @@ String File::parseAbsolutePath (const String& p) | |||||
// Windows.. | // Windows.. | ||||
String path (p.replaceCharacter ('/', '\\')); | String path (p.replaceCharacter ('/', '\\')); | ||||
if (path.contains ("\\..\\")) | |||||
path = removeEllipsis (path); | |||||
if (path.startsWithChar (separator)) | if (path.startsWithChar (separator)) | ||||
{ | { | ||||
if (path[1] != separator) | if (path[1] != separator) | ||||
@@ -120,6 +139,9 @@ String File::parseAbsolutePath (const String& p) | |||||
String path (p); | String path (p); | ||||
if (path.contains ("/../")) | |||||
path = removeEllipsis (path); | |||||
if (path.startsWithChar ('~')) | if (path.startsWithChar ('~')) | ||||
{ | { | ||||
if (path[1] == separator || path[1] == 0) | if (path[1] == separator || path[1] == 0) | ||||
@@ -347,62 +369,69 @@ int64 File::hashCode64() const { return fullPath.hashCode64(); } | |||||
//============================================================================== | //============================================================================== | ||||
bool File::isAbsolutePath (StringRef path) | bool File::isAbsolutePath (StringRef path) | ||||
{ | { | ||||
return path.text[0] == separator | |||||
const juce_wchar firstChar = *(path.text); | |||||
return firstChar == separator | |||||
#if JUCE_WINDOWS | #if JUCE_WINDOWS | ||||
|| (path.isNotEmpty() && path.text[1] == ':'); | |||||
|| (firstChar != 0 && path.text[1] == ':'); | |||||
#else | #else | ||||
|| path.text[0] == '~'; | |||||
|| firstChar == '~'; | |||||
#endif | #endif | ||||
} | } | ||||
File File::getChildFile (StringRef relativePath) const | 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 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 | #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); | const int lastSlash = path.lastIndexOfChar (separator); | ||||
if (lastSlash >= 0) | if (lastSlash >= 0) | ||||
path = path.substring (0, lastSlash); | path = path.substring (0, lastSlash); | ||||
relativePath = relativePath.text + (thirdChar == 0 ? 2 : 3); | |||||
while (*r == separator) // ignore duplicate slashes | |||||
++r; | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
r = lastPos; | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
else if (secondChar == separator) | |||||
else if (secondChar == separator || secondChar == 0) // remove "./" | |||||
{ | { | ||||
relativePath = relativePath.text + 2; | |||||
while (*r == separator) // ignore duplicate slashes | |||||
++r; | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
r = lastPos; | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
return File (addTrailingSeparator (path) + relativePath); | |||||
path = addTrailingSeparator (path); | |||||
path.appendCharPointer (r); | |||||
return File (path); | |||||
} | } | ||||
File File::getSiblingFile (StringRef fileName) const | File File::getSiblingFile (StringRef fileName) const | ||||
@@ -1020,6 +1049,17 @@ public: | |||||
expect (tempFile.getSiblingFile ("foo").isAChildOf (temp)); | expect (tempFile.getSiblingFile ("foo").isAChildOf (temp)); | ||||
expect (tempFile.hasWriteAccess()); | 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); | FileOutputStream fo (tempFile); | ||||
fo.write ("0123456789", 10); | fo.write ("0123456789", 10); | ||||
@@ -48,7 +48,7 @@ public: | |||||
//============================================================================== | //============================================================================== | ||||
/** Creates an (invalid) file object. | /** 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 | an empty string, and comparing the file to File::nonexistent will return | ||||
true. | true. | ||||
@@ -66,10 +66,10 @@ public: | |||||
//============================================================================== | //============================================================================== | ||||
/** Returns true if the filename matches one of the patterns specified. */ | /** 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. */ | /** This always returns true. */ | ||||
bool isDirectorySuitable (const File& file) const; | |||||
bool isDirectorySuitable (const File& file) const override; | |||||
private: | private: | ||||
//============================================================================== | //============================================================================== | ||||
@@ -44,7 +44,8 @@ | |||||
#define JUCE_JS_KEYWORDS(X) \ | #define JUCE_JS_KEYWORDS(X) \ | ||||
X(var, "var") X(if_, "if") X(else_, "else") X(do_, "do") X(null_, "null") \ | 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(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 | namespace TokenTypes | ||||
{ | { | ||||
@@ -71,6 +72,7 @@ struct JavascriptEngine::RootObject : public DynamicObject | |||||
setMethod ("trace", trace); | setMethod ("trace", trace); | ||||
setMethod ("charToInt", charToInt); | setMethod ("charToInt", charToInt); | ||||
setMethod ("parseInt", IntegerClass::parseInt); | setMethod ("parseInt", IntegerClass::parseInt); | ||||
setMethod ("typeof", typeof_internal); | |||||
} | } | ||||
Time timeout; | Time timeout; | ||||
@@ -97,12 +99,13 @@ struct JavascriptEngine::RootObject : public DynamicObject | |||||
&& (((a.isUndefined() || a.isVoid()) && (b.isUndefined() || b.isVoid())) || a == b); | && (((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 | struct CodeLocation | ||||
@@ -1079,7 +1082,7 @@ struct JavascriptEngine::RootObject : public DynamicObject | |||||
if (matchIf (TokenTypes::while_)) return parseDoOrWhileLoop (false); | if (matchIf (TokenTypes::while_)) return parseDoOrWhileLoop (false); | ||||
if (matchIf (TokenTypes::do_)) return parseDoOrWhileLoop (true); | if (matchIf (TokenTypes::do_)) return parseDoOrWhileLoop (true); | ||||
if (matchIf (TokenTypes::for_)) return parseForLoop(); | 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::break_)) return new BreakStatement (location); | ||||
if (matchIf (TokenTypes::continue_)) return new ContinueStatement (location); | if (matchIf (TokenTypes::continue_)) return new ContinueStatement (location); | ||||
if (matchIf (TokenTypes::function)) return parseFunction(); | if (matchIf (TokenTypes::function)) return parseFunction(); | ||||
@@ -1111,6 +1114,16 @@ struct JavascriptEngine::RootObject : public DynamicObject | |||||
return s.release(); | 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() | Statement* parseVar() | ||||
{ | { | ||||
ScopedPointer<VarStatement> s (new VarStatement (location)); | 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)); | 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() | Expression* parseUnary() | ||||
{ | { | ||||
if (matchIf (TokenTypes::minus)) { ExpPtr a (new LiteralValue (location, (int) 0)), b (parseUnary()); return new SubtractionOp (location, a, b); } | 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::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::plusplus)) return parsePreIncDec<AdditionOp>(); | ||||
if (matchIf (TokenTypes::minusminus)) return parsePreIncDec<SubtractionOp>(); | if (matchIf (TokenTypes::minusminus)) return parsePreIncDec<SubtractionOp>(); | ||||
if (matchIf (TokenTypes::typeof_)) return parseTypeof(); | |||||
return parseFactor(); | return parseFactor(); | ||||
} | } | ||||
@@ -1478,6 +1500,7 @@ struct JavascriptEngine::RootObject : public DynamicObject | |||||
setMethod ("contains", contains); | setMethod ("contains", contains); | ||||
setMethod ("remove", remove); | setMethod ("remove", remove); | ||||
setMethod ("join", join); | setMethod ("join", join); | ||||
setMethod ("push", push); | |||||
} | } | ||||
static Identifier getClassName() { static const Identifier i ("Array"); return i; } | 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)); | 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 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 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) | static var exec (Args a) | ||||
{ | { | ||||
if (RootObject* root = dynamic_cast<RootObject*> (a.thisObject.getObject())) | if (RootObject* root = dynamic_cast<RootObject*> (a.thisObject.getObject())) | ||||
@@ -35,7 +35,10 @@ | |||||
#error "Incorrect use of JUCE cpp file" | #error "Incorrect use of JUCE cpp file" | ||||
#endif | #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 "juce_core.h" | ||||
#include <locale> | #include <locale> | ||||
@@ -80,6 +83,7 @@ | |||||
#if JUCE_LINUX | #if JUCE_LINUX | ||||
#include <langinfo.h> | #include <langinfo.h> | ||||
#include <ifaddrs.h> | #include <ifaddrs.h> | ||||
#include <sys/resource.h> | |||||
#if JUCE_USE_CURL | #if JUCE_USE_CURL | ||||
#include <curl/curl.h> | #include <curl/curl.h> | ||||
@@ -140,6 +144,7 @@ namespace juce | |||||
#include "maths/juce_Expression.cpp" | #include "maths/juce_Expression.cpp" | ||||
#include "maths/juce_Random.cpp" | #include "maths/juce_Random.cpp" | ||||
#include "memory/juce_MemoryBlock.cpp" | #include "memory/juce_MemoryBlock.cpp" | ||||
#include "misc/juce_RuntimePermissions.cpp" | |||||
#include "misc/juce_Result.cpp" | #include "misc/juce_Result.cpp" | ||||
#include "misc/juce_Uuid.cpp" | #include "misc/juce_Uuid.cpp" | ||||
#include "network/juce_MACAddress.cpp" | #include "network/juce_MACAddress.cpp" | ||||
@@ -180,10 +185,6 @@ namespace juce | |||||
#include "files/juce_WildcardFileFilter.cpp" | #include "files/juce_WildcardFileFilter.cpp" | ||||
//============================================================================== | //============================================================================== | ||||
#if JUCE_MAC || JUCE_IOS | |||||
#include "native/juce_osx_ObjCHelpers.h" | |||||
#endif | |||||
#if JUCE_ANDROID | #if JUCE_ANDROID | ||||
#include "native/juce_android_JNIHelpers.h" | #include "native/juce_android_JNIHelpers.h" | ||||
#endif | #endif | ||||
@@ -203,7 +204,6 @@ namespace juce | |||||
//============================================================================== | //============================================================================== | ||||
#elif JUCE_WINDOWS | #elif JUCE_WINDOWS | ||||
#include "native/juce_win32_ComSmartPtr.h" | |||||
#include "native/juce_win32_Files.cpp" | #include "native/juce_win32_Files.cpp" | ||||
#include "native/juce_win32_Network.cpp" | #include "native/juce_win32_Network.cpp" | ||||
#include "native/juce_win32_Registry.cpp" | #include "native/juce_win32_Registry.cpp" | ||||
@@ -229,6 +229,7 @@ namespace juce | |||||
#include "native/juce_android_Network.cpp" | #include "native/juce_android_Network.cpp" | ||||
#include "native/juce_android_SystemStats.cpp" | #include "native/juce_android_SystemStats.cpp" | ||||
#include "native/juce_android_Threads.cpp" | #include "native/juce_android_Threads.cpp" | ||||
#include "native/juce_android_RuntimePermissions.cpp" | |||||
#endif | #endif | ||||
@@ -41,7 +41,7 @@ | |||||
#include "system/juce_TargetPlatform.h" | #include "system/juce_TargetPlatform.h" | ||||
//============================================================================= | |||||
//============================================================================== | |||||
/** Config: JUCE_FORCE_DEBUG | /** Config: JUCE_FORCE_DEBUG | ||||
Normally, JUCE_DEBUG is set to 1 or 0 based on compiler and project settings, | Normally, JUCE_DEBUG is set to 1 or 0 based on compiler and project settings, | ||||
@@ -51,7 +51,7 @@ | |||||
//#define JUCE_FORCE_DEBUG 0 | //#define JUCE_FORCE_DEBUG 0 | ||||
#endif | #endif | ||||
//============================================================================= | |||||
//============================================================================== | |||||
/** Config: JUCE_LOG_ASSERTIONS | /** Config: JUCE_LOG_ASSERTIONS | ||||
If this flag is enabled, the jassert and jassertfalse macros will always use Logger::writeToLog() | If this flag is enabled, the jassert and jassertfalse macros will always use Logger::writeToLog() | ||||
@@ -71,7 +71,7 @@ | |||||
#endif | #endif | ||||
#endif | #endif | ||||
//============================================================================= | |||||
//============================================================================== | |||||
/** Config: JUCE_CHECK_MEMORY_LEAKS | /** Config: JUCE_CHECK_MEMORY_LEAKS | ||||
Enables a memory-leak check for certain objects when the app terminates. See the LeakedObjectDetector | 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 | #define JUCE_CHECK_MEMORY_LEAKS 1 | ||||
#endif | #endif | ||||
//============================================================================= | |||||
//============================================================================== | |||||
/** Config: JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES | /** Config: JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES | ||||
In a Visual C++ build, this can be used to stop the required system libs being | 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 | #define JUCE_STRING_UTF_TYPE 8 | ||||
#endif | #endif | ||||
//============================================================================= | |||||
//============================================================================= | |||||
//============================================================================== | |||||
//============================================================================== | |||||
#if JUCE_CORE_INCLUDE_NATIVE_HEADERS | |||||
#include "native/juce_BasicNativeHeaders.h" | |||||
#endif | |||||
#include "system/juce_StandardHeader.h" | #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 "threads/juce_CriticalSection.h" | ||||
#include "maths/juce_Range.h" | #include "maths/juce_Range.h" | ||||
#include "maths/juce_NormalisableRange.h" | #include "maths/juce_NormalisableRange.h" | ||||
#include "maths/juce_StatisticsAccumulator.h" | |||||
#include "containers/juce_ElementComparator.h" | #include "containers/juce_ElementComparator.h" | ||||
#include "containers/juce_ArrayAllocationBase.h" | #include "containers/juce_ArrayAllocationBase.h" | ||||
#include "containers/juce_Array.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_BigInteger.h" | ||||
#include "maths/juce_Expression.h" | #include "maths/juce_Expression.h" | ||||
#include "maths/juce_Random.h" | #include "maths/juce_Random.h" | ||||
#include "misc/juce_RuntimePermissions.h" | |||||
#include "misc/juce_Uuid.h" | #include "misc/juce_Uuid.h" | ||||
#include "misc/juce_WindowsRegistry.h" | #include "misc/juce_WindowsRegistry.h" | ||||
#include "system/juce_SystemStats.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 "containers/juce_PropertySet.h" | ||||
#include "memory/juce_SharedResourcePointer.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 | #ifndef DOXYGEN | ||||
/* | /* | ||||
As the very long class names here try to explain, the purpose of this code is to cause | 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) | 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); | return 31 - __builtin_clz (n); | ||||
#elif JUCE_USE_MSVC_INTRINSICS | #elif JUCE_USE_MSVC_INTRINSICS | ||||
unsigned long highest; | unsigned long highest; | ||||
@@ -324,9 +324,9 @@ template <> | |||||
inline float juce_hypot (float a, float b) noexcept | inline float juce_hypot (float a, float b) noexcept | ||||
{ | { | ||||
#if JUCE_MSVC | #if JUCE_MSVC | ||||
return (_hypotf (a, b)); | |||||
return _hypotf (a, b); | |||||
#else | #else | ||||
return (hypotf (a, b)); | |||||
return hypotf (a, b); | |||||
#endif | #endif | ||||
} | } | ||||
#endif | #endif | ||||
@@ -356,12 +356,16 @@ const float float_Pi = 3.14159265358979323846f; | |||||
/** Converts an angle in degrees to radians. */ | /** 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. */ | /** 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 | #endif | ||||
//============================================================================== | //============================================================================== | ||||
#elif (JUCE_GCC || JUCE_CLANG) && ! JUCE_MSVC | |||||
#elif JUCE_GCC || JUCE_CLANG | |||||
#define JUCE_ATOMICS_GCC 1 // GCC with intrinsics | #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) | #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 | #if JUCE_MAC || JUCE_IOS | ||||
return OSSwapInt32 (n); | 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)); | asm("bswap %%eax" : "=a"(n) : "a"(n)); | ||||
return n; | return n; | ||||
#elif JUCE_USE_MSVC_INTRINSICS | #elif JUCE_USE_MSVC_INTRINSICS | ||||
@@ -180,6 +180,12 @@ private: | |||||
//============================================================================== | //============================================================================== | ||||
ScopedPointer<ObjectType> object; | ScopedPointer<ObjectType> object; | ||||
bool shouldDelete; | 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.Context; | ||||
import android.content.Intent; | import android.content.Intent; | ||||
import android.content.res.Configuration; | import android.content.res.Configuration; | ||||
import android.content.pm.PackageInfo; | |||||
import android.content.pm.PackageManager; | import android.content.pm.PackageManager; | ||||
import android.net.Uri; | import android.net.Uri; | ||||
import android.os.Bundle; | import android.os.Bundle; | ||||
import android.os.Looper; | import android.os.Looper; | ||||
import android.os.Handler; | import android.os.Handler; | ||||
import android.os.Build; | |||||
import android.os.Process; | |||||
import android.os.ParcelUuid; | import android.os.ParcelUuid; | ||||
import android.os.Environment; | |||||
import android.view.*; | import android.view.*; | ||||
import android.view.inputmethod.BaseInputConnection; | import android.view.inputmethod.BaseInputConnection; | ||||
import android.view.inputmethod.EditorInfo; | import android.view.inputmethod.EditorInfo; | ||||
@@ -49,19 +49,16 @@ import android.text.InputType; | |||||
import android.util.DisplayMetrics; | import android.util.DisplayMetrics; | ||||
import android.util.Log; | import android.util.Log; | ||||
import java.lang.Runnable; | 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.io.*; | ||||
import java.net.URL; | import java.net.URL; | ||||
import java.net.HttpURLConnection; | import java.net.HttpURLConnection; | ||||
import android.media.AudioManager; | import android.media.AudioManager; | ||||
import android.media.MediaScannerConnection; | import android.media.MediaScannerConnection; | ||||
import android.media.MediaScannerConnection.MediaScannerConnectionClient; | 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! | $$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"); | 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 | public static class MidiPortID extends Object | ||||
{ | { | ||||
@@ -132,10 +196,13 @@ public class JuceAppActivity extends Activity | |||||
super.onCreate (savedInstanceState); | super.onCreate (savedInstanceState); | ||||
isScreenSaverEnabled = true; | isScreenSaverEnabled = true; | ||||
hideActionBar(); | |||||
viewHolder = new ViewHolder (this); | viewHolder = new ViewHolder (this); | ||||
setContentView (viewHolder); | setContentView (viewHolder); | ||||
setVolumeControlStream (AudioManager.STREAM_MUSIC); | setVolumeControlStream (AudioManager.STREAM_MUSIC); | ||||
permissionCallbackPtrMap = new HashMap<Integer, Long>(); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -174,6 +241,49 @@ public class JuceAppActivity extends Activity | |||||
getApplicationInfo().dataDir); | 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 launchApp (String appFile, String appDataDir); | ||||
private native void quitApp(); | private native void quitApp(); | ||||
@@ -691,7 +801,7 @@ public class JuceAppActivity extends Activity | |||||
int format, int width, int height); | int format, int width, int height); | ||||
} | } | ||||
public NativeSurfaceView createNativeSurfaceView(long nativeSurfacePtr) | |||||
public NativeSurfaceView createNativeSurfaceView (long nativeSurfacePtr) | |||||
{ | { | ||||
return new NativeSurfaceView (this, nativeSurfacePtr); | return new NativeSurfaceView (this, nativeSurfacePtr); | ||||
} | } | ||||
@@ -917,6 +1027,17 @@ public class JuceAppActivity extends Activity | |||||
: locale.getDisplayLanguage (java.util.Locale.US); | : 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 | private final class SingleMediaScanner implements MediaScannerConnectionClient | ||||
{ | { | ||||
@@ -1041,23 +1162,24 @@ public class JuceAppActivity extends Activity | |||||
return null; | return null; | ||||
java.lang.reflect.Method method; | java.lang.reflect.Method method; | ||||
try { | |||||
try | |||||
{ | |||||
method = obj.getClass().getMethod ("getProperty", String.class); | 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) | if (method == null) | ||||
return null; | return null; | ||||
try { | |||||
try | |||||
{ | |||||
return (String) method.invoke (obj, property); | 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; | return null; | ||||
} | } | ||||
@@ -1075,8 +1197,9 @@ public class JuceAppActivity extends Activity | |||||
private static class JuceThread extends Thread | 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; | _this = host; | ||||
} | } | ||||
@@ -1089,9 +1212,8 @@ public class JuceAppActivity extends Activity | |||||
private long _this; | 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); | |||||
} | } | ||||
} | } |