@@ -25,6 +25,4 @@ | |||
#include "modules/juce_gui_extra/juce_gui_extra.h" | |||
#include "modules/juce_tracktion_marketplace/juce_tracktion_marketplace.h" | |||
#include "modules/juce_audio_plugin_client/utility/juce_CheckSettingMacros.h" | |||
#endif // JUCE_PLUGIN_MAIN_H_INCLUDED |
@@ -139,7 +139,7 @@ | |||
@see VSTPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU, JUCE_PLUGINHOST_VST3 | |||
*/ | |||
#define JUCE_PLUGINHOST_VST 0 | |||
#define JUCE_PLUGINHOST_VST 1 | |||
/** Config: JUCE_PLUGINHOST_VST3 | |||
Enables the VST3 audio plugin hosting classes. This requires the Steinberg VST3 SDK to be | |||
@@ -29,17 +29,6 @@ namespace FloatVectorHelpers | |||
#define JUCE_INCREMENT_DEST dest += (16 / sizeof (*dest)); | |||
#if JUCE_USE_SSE_INTRINSICS | |||
static bool sse2Present = false; | |||
static bool isSSE2Available() noexcept | |||
{ | |||
if (sse2Present) | |||
return true; | |||
sse2Present = SystemStats::hasSSE2(); | |||
return sse2Present; | |||
} | |||
inline static bool isAligned (const void* p) noexcept | |||
{ | |||
return (((pointer_sized_int) p) & 15) == 0; | |||
@@ -113,7 +102,6 @@ namespace FloatVectorHelpers | |||
#define JUCE_BEGIN_VEC_OP \ | |||
typedef FloatVectorHelpers::ModeType<sizeof(*dest)>::Mode Mode; \ | |||
if (FloatVectorHelpers::isSSE2Available()) \ | |||
{ \ | |||
const int numLongOps = num / Mode::numParallel; | |||
@@ -372,11 +360,7 @@ namespace FloatVectorHelpers | |||
{ | |||
int numLongOps = num / Mode::numParallel; | |||
#if JUCE_USE_SSE_INTRINSICS | |||
if (numLongOps > 1 && isSSE2Available()) | |||
#else | |||
if (numLongOps > 1) | |||
#endif | |||
{ | |||
ParallelType val; | |||
@@ -446,11 +430,7 @@ namespace FloatVectorHelpers | |||
{ | |||
int numLongOps = num / Mode::numParallel; | |||
#if JUCE_USE_SSE_INTRINSICS | |||
if (numLongOps > 1 && isSSE2Available()) | |||
#else | |||
if (numLongOps > 1) | |||
#endif | |||
{ | |||
ParallelType mn, mx; | |||
@@ -1002,12 +982,19 @@ double JUCE_CALLTYPE FloatVectorOperations::findMaximum (const double* src, int | |||
void JUCE_CALLTYPE FloatVectorOperations::enableFlushToZeroMode (bool shouldEnable) noexcept | |||
{ | |||
#if JUCE_USE_SSE_INTRINSICS | |||
if (FloatVectorHelpers::isSSE2Available()) | |||
_MM_SET_FLUSH_ZERO_MODE (shouldEnable ? _MM_FLUSH_ZERO_ON : _MM_FLUSH_ZERO_OFF); | |||
_MM_SET_FLUSH_ZERO_MODE (shouldEnable ? _MM_FLUSH_ZERO_ON : _MM_FLUSH_ZERO_OFF); | |||
#endif | |||
ignoreUnused (shouldEnable); | |||
} | |||
void JUCE_CALLTYPE FloatVectorOperations::disableDenormalisedNumberSupport() noexcept | |||
{ | |||
#if JUCE_USE_SSE_INTRINSICS | |||
const int mxcsr = _mm_getcsr(); | |||
_mm_setcsr (mxcsr | 0x8040); // add the DAZ and FZ bits | |||
#endif | |||
} | |||
//============================================================================== | |||
//============================================================================== | |||
#if JUCE_UNIT_TESTS | |||
@@ -198,6 +198,12 @@ public: | |||
Effectively, this is a wrapper around a call to _MM_SET_FLUSH_ZERO_MODE | |||
*/ | |||
static void JUCE_CALLTYPE enableFlushToZeroMode (bool shouldEnable) noexcept; | |||
/** On Intel CPUs, this method enables the SSE flush-to-zero and denormalised-are-zero modes. | |||
This effectively sets the DAZ and FZ bits of the MXCSR register. It's a convenient thing to | |||
call before audio processing code where you really want to avoid denormalisation performance hits. | |||
*/ | |||
static void JUCE_CALLTYPE disableDenormalisedNumberSupport() noexcept; | |||
}; | |||
@@ -0,0 +1,63 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
struct CatmullRomAlgorithm | |||
{ | |||
static forcedinline float valueAtOffset (const float* const inputs, const float offset) noexcept | |||
{ | |||
const float y0 = inputs[3]; | |||
const float y1 = inputs[2]; | |||
const float y2 = inputs[1]; | |||
const float y3 = inputs[0]; | |||
const float halfY0 = 0.5f * y0; | |||
const float halfY3 = 0.5f * y3; | |||
return y1 + offset * ((0.5f * y2 - halfY0) | |||
+ (offset * (((y0 + 2.0f * y2) - (halfY3 + 2.5f * y1)) | |||
+ (offset * ((halfY3 + 1.5f * y1) - (halfY0 + 1.5f * y2)))))); | |||
} | |||
}; | |||
CatmullRomInterpolator::CatmullRomInterpolator() noexcept { reset(); } | |||
CatmullRomInterpolator::~CatmullRomInterpolator() noexcept {} | |||
void CatmullRomInterpolator::reset() noexcept | |||
{ | |||
subSamplePos = 1.0; | |||
for (int i = 0; i < numElementsInArray (lastInputSamples); ++i) | |||
lastInputSamples[i] = 0; | |||
} | |||
int CatmullRomInterpolator::process (double actualRatio, const float* in, float* out, int numOut) noexcept | |||
{ | |||
return interpolate<CatmullRomAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut); | |||
} | |||
int CatmullRomInterpolator::processAdding (double actualRatio, const float* in, float* out, int numOut, float gain) noexcept | |||
{ | |||
return interpolateAdding<CatmullRomAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, gain); | |||
} |
@@ -0,0 +1,89 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | |||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
/** | |||
Interpolator for resampling a stream of floats using Catmull-Rom interpolation. | |||
Note that the resampler is stateful, so when there's a break in the continuity | |||
of the input stream you're feeding it, you should call reset() before feeding | |||
it any new data. And like with any other stateful filter, if you're resampling | |||
multiple channels, make sure each one uses its own CatmullRomInterpolator | |||
object. | |||
@see LagrangeInterpolator | |||
*/ | |||
class JUCE_API CatmullRomInterpolator | |||
{ | |||
public: | |||
CatmullRomInterpolator() noexcept; | |||
~CatmullRomInterpolator() noexcept; | |||
/** Resets the state of the interpolator. | |||
Call this when there's a break in the continuity of the input data stream. | |||
*/ | |||
void reset() noexcept; | |||
/** Resamples a stream of samples. | |||
@param speedRatio the number of input samples to use for each output sample | |||
@param inputSamples the source data to read from. This must contain at | |||
least (speedRatio * numOutputSamplesToProduce) samples. | |||
@param outputSamples the buffer to write the results into | |||
@param numOutputSamplesToProduce the number of output samples that should be created | |||
@returns the actual number of input samples that were used | |||
*/ | |||
int process (double speedRatio, | |||
const float* inputSamples, | |||
float* outputSamples, | |||
int numOutputSamplesToProduce) noexcept; | |||
/** Resamples a stream of samples, adding the results to the output data | |||
with a gain. | |||
@param speedRatio the number of input samples to use for each output sample | |||
@param inputSamples the source data to read from. This must contain at | |||
least (speedRatio * numOutputSamplesToProduce) samples. | |||
@param outputSamples the buffer to write the results to - the result values will be added | |||
to any pre-existing data in this buffer after being multiplied by | |||
the gain factor | |||
@param numOutputSamplesToProduce the number of output samples that should be created | |||
@param gain a gain factor to multiply the resulting samples by before | |||
adding them to the destination buffer | |||
@returns the actual number of input samples that were used | |||
*/ | |||
int processAdding (double speedRatio, | |||
const float* inputSamples, | |||
float* outputSamples, | |||
int numOutputSamplesToProduce, | |||
float gain) noexcept; | |||
private: | |||
float lastInputSamples[5]; | |||
double subSamplePos; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CatmullRomInterpolator) | |||
}; |
@@ -22,185 +22,179 @@ | |||
============================================================================== | |||
*/ | |||
namespace LagrangeHelpers | |||
namespace | |||
{ | |||
template <int k> | |||
struct ResampleHelper | |||
{ | |||
static forcedinline void calc (float& a, float b) { a *= b * (1.0f / k); } | |||
}; | |||
template<> | |||
struct ResampleHelper <0> | |||
{ | |||
static forcedinline void calc (float&, float) {} | |||
}; | |||
template <int k> | |||
static forcedinline float calcCoefficient (float input, const float offset) noexcept | |||
static forcedinline void pushInterpolationSample (float* lastInputSamples, const float newValue) noexcept | |||
{ | |||
ResampleHelper <0 - k>::calc (input, -2.0f - offset); | |||
ResampleHelper <1 - k>::calc (input, -1.0f - offset); | |||
ResampleHelper <2 - k>::calc (input, 0.0f - offset); | |||
ResampleHelper <3 - k>::calc (input, 1.0f - offset); | |||
ResampleHelper <4 - k>::calc (input, 2.0f - offset); | |||
return input; | |||
lastInputSamples[4] = lastInputSamples[3]; | |||
lastInputSamples[3] = lastInputSamples[2]; | |||
lastInputSamples[2] = lastInputSamples[1]; | |||
lastInputSamples[1] = lastInputSamples[0]; | |||
lastInputSamples[0] = newValue; | |||
} | |||
static forcedinline float valueAtOffset (const float* const inputs, const float offset) noexcept | |||
{ | |||
return calcCoefficient<0> (inputs[4], offset) | |||
+ calcCoefficient<1> (inputs[3], offset) | |||
+ calcCoefficient<2> (inputs[2], offset) | |||
+ calcCoefficient<3> (inputs[1], offset) | |||
+ calcCoefficient<4> (inputs[0], offset); | |||
} | |||
static forcedinline void push (float* inputs, const float newValue) noexcept | |||
{ | |||
inputs[4] = inputs[3]; | |||
inputs[3] = inputs[2]; | |||
inputs[2] = inputs[1]; | |||
inputs[1] = inputs[0]; | |||
inputs[0] = newValue; | |||
} | |||
} | |||
//============================================================================== | |||
LagrangeInterpolator::LagrangeInterpolator() { reset(); } | |||
LagrangeInterpolator::~LagrangeInterpolator() {} | |||
void LagrangeInterpolator::reset() noexcept | |||
{ | |||
subSamplePos = 1.0; | |||
for (int i = 0; i < numElementsInArray (lastInputSamples); ++i) | |||
lastInputSamples[i] = 0; | |||
} | |||
int LagrangeInterpolator::process (const double actualRatio, const float* in, | |||
float* out, const int numOut) noexcept | |||
{ | |||
if (actualRatio == 1.0) | |||
static forcedinline void pushInterpolationSamples (float* lastInputSamples, const float* input, int numOut) noexcept | |||
{ | |||
memcpy (out, in, (size_t) numOut * sizeof (float)); | |||
if (numOut >= 4) | |||
if (numOut >= 5) | |||
{ | |||
const float* end = in + numOut; | |||
for (int i = 0; i < 4; ++i) | |||
lastInputSamples[i] = *--end; | |||
for (int i = 0; i < 5; ++i) | |||
lastInputSamples[i] = input[--numOut]; | |||
} | |||
else | |||
{ | |||
for (int i = 0; i < numOut; ++i) | |||
LagrangeHelpers::push (lastInputSamples, in[i]); | |||
pushInterpolationSample (lastInputSamples, input[i]); | |||
} | |||
return numOut; | |||
} | |||
const float* const originalIn = in; | |||
double pos = subSamplePos; | |||
if (actualRatio < 1.0) | |||
template <typename InterpolatorType> | |||
static int interpolate (float* lastInputSamples, double& subSamplePos, const double actualRatio, | |||
const float* in, float* out, const int numOut) noexcept | |||
{ | |||
for (int i = numOut; --i >= 0;) | |||
if (actualRatio == 1.0) | |||
{ | |||
if (pos >= 1.0) | |||
memcpy (out, in, (size_t) numOut * sizeof (float)); | |||
pushInterpolationSamples (lastInputSamples, in, numOut); | |||
return numOut; | |||
} | |||
const float* const originalIn = in; | |||
double pos = subSamplePos; | |||
if (actualRatio < 1.0) | |||
{ | |||
for (int i = numOut; --i >= 0;) | |||
{ | |||
LagrangeHelpers::push (lastInputSamples, *in++); | |||
pos -= 1.0; | |||
if (pos >= 1.0) | |||
{ | |||
pushInterpolationSample (lastInputSamples, *in++); | |||
pos -= 1.0; | |||
} | |||
*out++ = InterpolatorType::valueAtOffset (lastInputSamples, (float) pos); | |||
pos += actualRatio; | |||
} | |||
*out++ = LagrangeHelpers::valueAtOffset (lastInputSamples, (float) pos); | |||
pos += actualRatio; | |||
} | |||
} | |||
else | |||
{ | |||
for (int i = numOut; --i >= 0;) | |||
else | |||
{ | |||
while (pos < actualRatio) | |||
for (int i = numOut; --i >= 0;) | |||
{ | |||
LagrangeHelpers::push (lastInputSamples, *in++); | |||
pos += 1.0; | |||
while (pos < actualRatio) | |||
{ | |||
pushInterpolationSample (lastInputSamples, *in++); | |||
pos += 1.0; | |||
} | |||
pos -= actualRatio; | |||
*out++ = InterpolatorType::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos)); | |||
} | |||
pos -= actualRatio; | |||
*out++ = LagrangeHelpers::valueAtOffset (lastInputSamples, 1.0f - (float) pos); | |||
} | |||
} | |||
subSamplePos = pos; | |||
return (int) (in - originalIn); | |||
} | |||
subSamplePos = pos; | |||
return (int) (in - originalIn); | |||
} | |||
int LagrangeInterpolator::processAdding (const double actualRatio, const float* in, | |||
float* out, const int numOut, const float gain) noexcept | |||
{ | |||
if (actualRatio == 1.0) | |||
template <typename InterpolatorType> | |||
static int interpolateAdding (float* lastInputSamples, double& subSamplePos, const double actualRatio, | |||
const float* in, float* out, const int numOut, const float gain) noexcept | |||
{ | |||
if (gain != 1.0f) | |||
{ | |||
for (int i = 0; i < numOut; ++i) | |||
out[i] += in[i] * gain; | |||
} | |||
else | |||
if (actualRatio == 1.0) | |||
{ | |||
for (int i = 0; i < numOut; ++i) | |||
out[i] += in[i]; | |||
FloatVectorOperations::addWithMultiply (out, in, gain, numOut); | |||
pushInterpolationSamples (lastInputSamples, in, numOut); | |||
return numOut; | |||
} | |||
if (numOut >= 4) | |||
{ | |||
const float* end = in + numOut; | |||
const float* const originalIn = in; | |||
double pos = subSamplePos; | |||
for (int i = 0; i < 4; ++i) | |||
lastInputSamples[i] = *--end; | |||
if (actualRatio < 1.0) | |||
{ | |||
for (int i = numOut; --i >= 0;) | |||
{ | |||
if (pos >= 1.0) | |||
{ | |||
pushInterpolationSample (lastInputSamples, *in++); | |||
pos -= 1.0; | |||
} | |||
*out++ += gain * InterpolatorType::valueAtOffset (lastInputSamples, (float) pos); | |||
pos += actualRatio; | |||
} | |||
} | |||
else | |||
{ | |||
for (int i = 0; i < numOut; ++i) | |||
LagrangeHelpers::push (lastInputSamples, in[i]); | |||
for (int i = numOut; --i >= 0;) | |||
{ | |||
while (pos < actualRatio) | |||
{ | |||
pushInterpolationSample (lastInputSamples, *in++); | |||
pos += 1.0; | |||
} | |||
pos -= actualRatio; | |||
*out++ += gain * InterpolatorType::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos)); | |||
} | |||
} | |||
return numOut; | |||
subSamplePos = pos; | |||
return (int) (in - originalIn); | |||
} | |||
} | |||
const float* const originalIn = in; | |||
double pos = subSamplePos; | |||
//============================================================================== | |||
template <int k> | |||
struct LagrangeResampleHelper | |||
{ | |||
static forcedinline void calc (float& a, float b) noexcept { a *= b * (1.0f / k); } | |||
}; | |||
if (actualRatio < 1.0) | |||
{ | |||
for (int i = numOut; --i >= 0;) | |||
{ | |||
if (pos >= 1.0) | |||
{ | |||
LagrangeHelpers::push (lastInputSamples, *in++); | |||
pos -= 1.0; | |||
} | |||
template<> | |||
struct LagrangeResampleHelper<0> | |||
{ | |||
static forcedinline void calc (float&, float) noexcept {} | |||
}; | |||
*out++ += gain * LagrangeHelpers::valueAtOffset (lastInputSamples, (float) pos); | |||
pos += actualRatio; | |||
} | |||
} | |||
else | |||
struct LagrangeAlgorithm | |||
{ | |||
static forcedinline float valueAtOffset (const float* const inputs, const float offset) noexcept | |||
{ | |||
for (int i = numOut; --i >= 0;) | |||
{ | |||
while (pos < actualRatio) | |||
{ | |||
LagrangeHelpers::push (lastInputSamples, *in++); | |||
pos += 1.0; | |||
} | |||
return calcCoefficient<0> (inputs[4], offset) | |||
+ calcCoefficient<1> (inputs[3], offset) | |||
+ calcCoefficient<2> (inputs[2], offset) | |||
+ calcCoefficient<3> (inputs[1], offset) | |||
+ calcCoefficient<4> (inputs[0], offset); | |||
} | |||
pos -= actualRatio; | |||
*out++ += gain * LagrangeHelpers::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos)); | |||
} | |||
template <int k> | |||
static forcedinline float calcCoefficient (float input, const float offset) noexcept | |||
{ | |||
LagrangeResampleHelper<0 - k>::calc (input, -2.0f - offset); | |||
LagrangeResampleHelper<1 - k>::calc (input, -1.0f - offset); | |||
LagrangeResampleHelper<2 - k>::calc (input, 0.0f - offset); | |||
LagrangeResampleHelper<3 - k>::calc (input, 1.0f - offset); | |||
LagrangeResampleHelper<4 - k>::calc (input, 2.0f - offset); | |||
return input; | |||
} | |||
}; | |||
LagrangeInterpolator::LagrangeInterpolator() noexcept { reset(); } | |||
LagrangeInterpolator::~LagrangeInterpolator() noexcept {} | |||
subSamplePos = pos; | |||
return (int) (in - originalIn); | |||
void LagrangeInterpolator::reset() noexcept | |||
{ | |||
subSamplePos = 1.0; | |||
for (int i = 0; i < numElementsInArray (lastInputSamples); ++i) | |||
lastInputSamples[i] = 0; | |||
} | |||
int LagrangeInterpolator::process (double actualRatio, const float* in, float* out, int numOut) noexcept | |||
{ | |||
return interpolate<LagrangeAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut); | |||
} | |||
int LagrangeInterpolator::processAdding (double actualRatio, const float* in, float* out, int numOut, float gain) noexcept | |||
{ | |||
return interpolateAdding<LagrangeAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, gain); | |||
} |
@@ -22,11 +22,7 @@ | |||
============================================================================== | |||
*/ | |||
#ifndef JUCE_LAGRANGEINTERPOLATOR_H_INCLUDED | |||
#define JUCE_LAGRANGEINTERPOLATOR_H_INCLUDED | |||
//============================================================================== | |||
/** | |||
Interpolator for resampling a stream of floats using 4-point lagrange interpolation. | |||
@@ -35,12 +31,14 @@ | |||
it any new data. And like with any other stateful filter, if you're resampling | |||
multiple channels, make sure each one uses its own LagrangeInterpolator | |||
object. | |||
@see CatmullRomInterpolator | |||
*/ | |||
class JUCE_API LagrangeInterpolator | |||
{ | |||
public: | |||
LagrangeInterpolator(); | |||
~LagrangeInterpolator(); | |||
LagrangeInterpolator() noexcept; | |||
~LagrangeInterpolator() noexcept; | |||
/** Resets the state of the interpolator. | |||
Call this when there's a break in the continuity of the input data stream. | |||
@@ -89,6 +87,3 @@ private: | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LagrangeInterpolator) | |||
}; | |||
#endif // JUCE_LAGRANGEINTERPOLATOR_H_INCLUDED |
@@ -49,7 +49,7 @@ public: | |||
{ | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
/** Reset to a new sample rate and ramp length. */ | |||
void reset (double sampleRate, double rampLengthInSeconds) noexcept | |||
{ | |||
@@ -59,7 +59,7 @@ public: | |||
countdown = 0; | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
/** Set a new target value. */ | |||
void setValue (FloatType newValue) noexcept | |||
{ | |||
@@ -75,7 +75,7 @@ public: | |||
} | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
/** Compute the next value. */ | |||
FloatType getNextValue() noexcept | |||
{ | |||
@@ -88,7 +88,7 @@ public: | |||
} | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
FloatType currentValue, target, step; | |||
int countdown, stepsToTarget; | |||
}; | |||
@@ -58,9 +58,7 @@ | |||
#endif | |||
#if (JUCE_MAC || JUCE_IOS) && JUCE_USE_VDSP_FRAMEWORK | |||
#define Point CarbonDummyPointName // (workaround to avoid definition of "Point" by old Carbon headers) | |||
#include <Accelerate/Accelerate.h> | |||
#undef Point | |||
#else | |||
#undef JUCE_USE_VDSP_FRAMEWORK | |||
#endif | |||
@@ -81,6 +79,7 @@ namespace juce | |||
#include "effects/juce_IIRFilter.cpp" | |||
#include "effects/juce_IIRFilterOld.cpp" | |||
#include "effects/juce_LagrangeInterpolator.cpp" | |||
#include "effects/juce_CatmullRomInterpolator.cpp" | |||
#include "effects/juce_FFT.cpp" | |||
#include "midi/juce_MidiBuffer.cpp" | |||
#include "midi/juce_MidiFile.cpp" | |||
@@ -27,7 +27,7 @@ | |||
#include "../juce_core/juce_core.h" | |||
//============================================================================= | |||
//============================================================================== | |||
namespace juce | |||
{ | |||
@@ -41,6 +41,7 @@ namespace juce | |||
#include "effects/juce_IIRFilter.h" | |||
#include "effects/juce_IIRFilterOld.h" | |||
#include "effects/juce_LagrangeInterpolator.h" | |||
#include "effects/juce_CatmullRomInterpolator.h" | |||
#include "effects/juce_FFT.h" | |||
#include "effects/juce_LinearSmoothedValue.h" | |||
#include "effects/juce_Reverb.h" | |||
@@ -371,7 +371,7 @@ int MidiMessage::getNoteNumber() const noexcept | |||
void MidiMessage::setNoteNumber (const int newNoteNumber) noexcept | |||
{ | |||
if (isNoteOnOrOff()) | |||
if (isNoteOnOrOff() || isAftertouch()) | |||
getData()[1] = (uint8) (newNoteNumber & 127); | |||
} | |||
@@ -156,23 +156,34 @@ struct MidiMessageSequenceSorter | |||
} | |||
}; | |||
void MidiMessageSequence::addSequence (const MidiMessageSequence& other, double timeAdjustment) | |||
{ | |||
for (int i = 0; i < other.list.size(); ++i) | |||
{ | |||
const MidiMessage& m = other.list.getUnchecked(i)->message; | |||
MidiEventHolder* const newOne = new MidiEventHolder (m); | |||
newOne->message.addToTimeStamp (timeAdjustment); | |||
list.add (newOne); | |||
} | |||
sort(); | |||
} | |||
void MidiMessageSequence::addSequence (const MidiMessageSequence& other, | |||
double timeAdjustment, | |||
double firstAllowableTime, | |||
double endOfAllowableDestTimes) | |||
{ | |||
firstAllowableTime -= timeAdjustment; | |||
endOfAllowableDestTimes -= timeAdjustment; | |||
for (int i = 0; i < other.list.size(); ++i) | |||
{ | |||
const MidiMessage& m = other.list.getUnchecked(i)->message; | |||
const double t = m.getTimeStamp(); | |||
const double t = m.getTimeStamp() + timeAdjustment; | |||
if (t >= firstAllowableTime && t < endOfAllowableDestTimes) | |||
{ | |||
MidiEventHolder* const newOne = new MidiEventHolder (m); | |||
newOne->message.setTimeStamp (timeAdjustment + t); | |||
newOne->message.setTimeStamp (t); | |||
list.add (newOne); | |||
} | |||
@@ -160,7 +160,6 @@ public: | |||
void deleteEvent (int index, bool deleteMatchingNoteUp); | |||
/** Merges another sequence into this one. | |||
Remember to call updateMatchedPairs() after using this method. | |||
@param other the sequence to add from | |||
@@ -178,6 +177,16 @@ public: | |||
double firstAllowableDestTime, | |||
double endOfAllowableDestTimes); | |||
/** Merges another sequence into this one. | |||
Remember to call updateMatchedPairs() after using this method. | |||
@param other the sequence to add from | |||
@param timeAdjustmentDelta an amount to add to the timestamps of the midi events | |||
as they are read from the other sequence | |||
*/ | |||
void addSequence (const MidiMessageSequence& other, | |||
double timeAdjustmentDelta); | |||
//============================================================================== | |||
/** Makes sure all the note-on and note-off pairs are up-to-date. | |||
@@ -331,7 +331,7 @@ public: | |||
} | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
void expectContainsRPN (const MidiBuffer& midiBuffer, | |||
int channel, | |||
int parameterNumber, | |||
@@ -343,7 +343,7 @@ private: | |||
expectContainsRPN (midiBuffer, expected); | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
void expectContainsRPN (const MidiBuffer& midiBuffer, MidiRPNMessage expected) | |||
{ | |||
MidiBuffer::Iterator iter (midiBuffer); | |||
@@ -26,7 +26,7 @@ | |||
#define JUCE_MIDIRPNDETECTOR_H_INCLUDED | |||
//========================================================================== | |||
//============================================================================== | |||
/** Represents a MIDI RPN (registered parameter number) or NRPN (non-registered | |||
parameter number) message. | |||
*/ | |||
@@ -77,7 +77,7 @@ public: | |||
*/ | |||
void reset() noexcept; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Takes the next in a stream of incoming MIDI CC messages and returns true | |||
if it forms the last of a sequence that makes an RPN or NPRN. | |||
@@ -91,7 +91,7 @@ public: | |||
MidiRPNMessage& result) noexcept; | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
struct ChannelState | |||
{ | |||
ChannelState() noexcept; | |||
@@ -104,7 +104,7 @@ private: | |||
bool isNRPN; | |||
}; | |||
//========================================================================== | |||
//============================================================================== | |||
ChannelState states[16]; | |||
JUCE_LEAK_DETECTOR (MidiRPNDetector) | |||
@@ -120,11 +120,11 @@ private: | |||
class JUCE_API MidiRPNGenerator | |||
{ | |||
public: | |||
//========================================================================== | |||
//============================================================================== | |||
/** Generates a MIDI sequence representing the given RPN or NRPN message. */ | |||
static MidiBuffer generate (MidiRPNMessage message); | |||
//========================================================================== | |||
//============================================================================== | |||
/** Generates a MIDI sequence representing an RPN or NRPN message with the | |||
given parameters. | |||
@@ -135,14 +135,6 @@ void MPEInstrument::removeListener (Listener* const listenerToRemove) noexcept | |||
listeners.remove (listenerToRemove); | |||
} | |||
MPEInstrument::Listener::Listener() | |||
{ | |||
} | |||
MPEInstrument::Listener::~Listener() | |||
{ | |||
} | |||
//============================================================================== | |||
void MPEInstrument::processNextMidiEvent (const MidiMessage& message) | |||
{ | |||
@@ -1976,7 +1968,7 @@ public: | |||
} | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
/* This mock class is used for unit testing whether the methods of | |||
MPEInstrument are called correctly. | |||
*/ | |||
@@ -2074,7 +2066,7 @@ private: | |||
ScopedPointer<MPENote> lastNoteFinished; | |||
private: | |||
//====================================================================== | |||
//============================================================================== | |||
void noteAdded (MPENote) override { noteAddedCallCounter++; } | |||
void notePressureChanged (MPENote) override { notePressureChangedCallCounter++; } | |||
@@ -2089,7 +2081,7 @@ private: | |||
} | |||
}; | |||
//========================================================================== | |||
//============================================================================== | |||
template <int initial7BitPressure, int initial14BitPitchbend, int initial7BitTimbre> | |||
class CustomInitialValuesTest : public MPEInstrument | |||
{ | |||
@@ -2109,7 +2101,7 @@ private: | |||
} | |||
}; | |||
//========================================================================== | |||
//============================================================================== | |||
void expectNote (MPENote noteToTest, | |||
int noteOnVelocity7Bit, | |||
int pressure7Bit, | |||
@@ -2141,7 +2133,7 @@ private: | |||
expect (std::fabs (expected - actual) < maxAbsoluteError); | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
MPEZoneLayout testLayout; | |||
}; | |||
@@ -68,7 +68,7 @@ public: | |||
/** Destructor. */ | |||
virtual ~MPEInstrument(); | |||
//========================================================================== | |||
//============================================================================== | |||
/** Returns the current zone layout of the instrument. | |||
This happens by value, to enforce thread-safety and class invariants. | |||
@@ -98,7 +98,7 @@ public: | |||
*/ | |||
bool isMasterChannel (int midiChannel) const noexcept; | |||
//========================================================================== | |||
//============================================================================== | |||
/** The MPE note tracking mode. In case there is more than one note playing | |||
simultaneously on the same MIDI channel, this determines which of these | |||
notes will be modulated by an incoming MPE message on that channel | |||
@@ -123,7 +123,7 @@ public: | |||
/** Set the MPE tracking mode for the timbre dimension. */ | |||
void setTimbreTrackingMode (TrackingMode modeToUse); | |||
//========================================================================== | |||
//============================================================================== | |||
/** Process a MIDI message and trigger the appropriate method calls | |||
(noteOn, noteOff etc.) | |||
@@ -132,7 +132,7 @@ public: | |||
*/ | |||
virtual void processNextMidiEvent (const MidiMessage& message); | |||
//========================================================================== | |||
//============================================================================== | |||
/** Request a note-on on the given channel, with the given initial note | |||
number and velocity. | |||
If the message arrives on a valid note channel, this will create a | |||
@@ -187,7 +187,7 @@ public: | |||
*/ | |||
void releaseAllNotes(); | |||
//========================================================================== | |||
//============================================================================== | |||
/** Returns the number of MPE notes currently played by the | |||
instrument. | |||
*/ | |||
@@ -221,7 +221,7 @@ public: | |||
*/ | |||
MPENote getMostRecentNoteOtherThan (MPENote otherThanThisNote) const noexcept; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Derive from this class to be informed about any changes in the expressive | |||
MIDI notes played by this instrument. | |||
@@ -230,14 +230,11 @@ public: | |||
Therefore you should never do heavy work such as graphics rendering etc. | |||
inside those callbacks. | |||
*/ | |||
class Listener | |||
class JUCE_API Listener | |||
{ | |||
public: | |||
/** Constructor. */ | |||
Listener(); | |||
/** Destructor. */ | |||
virtual ~Listener(); | |||
virtual ~Listener() {} | |||
/** Implement this callback to be informed whenever a new expressive | |||
MIDI note is triggered. | |||
@@ -278,14 +275,14 @@ public: | |||
virtual void noteReleased (MPENote finishedNote) = 0; | |||
}; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Adds a listener. */ | |||
void addListener (Listener* const listenerToAdd) noexcept; | |||
void addListener (Listener* listenerToAdd) noexcept; | |||
/** Removes a listener. */ | |||
void removeListener (Listener* const listenerToRemove) noexcept; | |||
void removeListener (Listener* listenerToRemove) noexcept; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Puts the instrument into legacy mode. | |||
As a side effect, this will discard all currently playing notes, | |||
and call noteReleased for all of them. | |||
@@ -324,7 +321,7 @@ public: | |||
void setLegacyModePitchbendRange (int pitchbendRange); | |||
protected: | |||
//========================================================================== | |||
//============================================================================== | |||
/** This method defines what initial pitchbend value should be used for newly | |||
triggered notes. The default is to use the last pitchbend value | |||
that has been received on the same MIDI channel (or no pitchbend | |||
@@ -354,7 +351,7 @@ protected: | |||
MPEValue midiNoteOnVelocity) const; | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
CriticalSection lock; | |||
Array<MPENote> notes; | |||
MPEZoneLayout zoneLayout; | |||
@@ -162,7 +162,7 @@ public: | |||
} | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
void testMidiBuffer (MidiBuffer& buffer, const uint8* expectedBytes, int expectedBytesSize) | |||
{ | |||
uint8 actualBytes[128] = { 0 }; | |||
@@ -171,7 +171,7 @@ private: | |||
expectEquals (std::memcmp (actualBytes, expectedBytes, (std::size_t) expectedBytesSize), 0); | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
void extractRawBinaryData (const MidiBuffer& midiBuffer, const uint8* bufferToCopyTo, std::size_t maxBytes) | |||
{ | |||
std::size_t pos = 0; | |||
@@ -103,7 +103,7 @@ class MPENoteTests : public UnitTest | |||
public: | |||
MPENoteTests() : UnitTest ("MPENote class") {} | |||
//========================================================================== | |||
//============================================================================== | |||
void runTest() override | |||
{ | |||
beginTest ("getFrequencyInHertz"); | |||
@@ -116,7 +116,7 @@ public: | |||
} | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
void expectEqualsWithinOneCent (double frequencyInHertzActual, | |||
double frequencyInHertzExpected) | |||
{ | |||
@@ -39,7 +39,7 @@ | |||
*/ | |||
struct JUCE_API MPENote | |||
{ | |||
//========================================================================== | |||
//============================================================================== | |||
enum KeyState | |||
{ | |||
off = 0, | |||
@@ -48,7 +48,7 @@ struct JUCE_API MPENote | |||
keyDownAndSustained = 3 | |||
}; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Constructor. | |||
@param midiChannel The MIDI channel of the note, between 2 and 16. | |||
@@ -88,7 +88,7 @@ struct JUCE_API MPENote | |||
/** Checks whether the MPE note is valid. */ | |||
bool isValid() const noexcept; | |||
//========================================================================== | |||
//============================================================================== | |||
// Invariants that define the note. | |||
/** A unique ID. Useful to distinguish the note from other simultaneously | |||
@@ -107,7 +107,7 @@ struct JUCE_API MPENote | |||
*/ | |||
uint8 initialNote; | |||
//========================================================================== | |||
//============================================================================== | |||
// The five dimensions of continuous expressive control | |||
/** The velocity ("strike") of the note-on. | |||
@@ -146,7 +146,7 @@ struct JUCE_API MPENote | |||
*/ | |||
MPEValue noteOffVelocity; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Current effective pitchbend of the note in units of semitones, relative | |||
to initialNote. You should use this to compute the actual effective pitch | |||
of the note. This value is computed and set by an MPEInstrument to the | |||
@@ -163,7 +163,7 @@ struct JUCE_API MPENote | |||
*/ | |||
KeyState keyState; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Returns the current frequency of the note in Hertz. This is the a sum of | |||
the initialNote and the totalPitchbendInSemitones, converted to Hertz. | |||
*/ | |||
@@ -55,7 +55,7 @@ | |||
class JUCE_API MPESynthesiser : public MPESynthesiserBase | |||
{ | |||
public: | |||
//========================================================================== | |||
//============================================================================== | |||
/** Constructor. | |||
You'll need to add some voices before it'll make any sound. | |||
@@ -75,7 +75,7 @@ public: | |||
/** Destructor. */ | |||
~MPESynthesiser(); | |||
//========================================================================== | |||
//============================================================================== | |||
/** Deletes all voices. */ | |||
void clearVoices(); | |||
@@ -116,7 +116,7 @@ public: | |||
*/ | |||
virtual void turnOffAllVoices (bool allowTailOff); | |||
//========================================================================== | |||
//============================================================================== | |||
/** If set to true, then the synth will try to take over an existing voice if | |||
it runs out and needs to play another note. | |||
@@ -128,7 +128,7 @@ public: | |||
/** Returns true if note-stealing is enabled. */ | |||
bool isVoiceStealingEnabled() const noexcept { return shouldStealVoices; } | |||
//========================================================================== | |||
//============================================================================== | |||
/** Tells the synthesiser what the sample rate is for the audio it's being used to render. | |||
This overrides the implementation in MPESynthesiserBase, to additionally | |||
@@ -137,7 +137,7 @@ public: | |||
*/ | |||
void setCurrentPlaybackSampleRate (double newRate) override; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Handle incoming MIDI events. | |||
This method will be called automatically according to the MIDI data passed | |||
@@ -238,7 +238,7 @@ protected: | |||
*/ | |||
virtual void noteKeyStateChanged (MPENote changedNote) override; | |||
//========================================================================== | |||
//============================================================================== | |||
/** This will simply call renderNextBlock for each currently active | |||
voice and fill the buffer with the sum. | |||
Override this method if you need to do more work to render your audio. | |||
@@ -255,7 +255,7 @@ protected: | |||
int startSample, | |||
int numSamples) override; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Searches through the voices to find one that's not currently playing, and | |||
which can play the given MPE note. | |||
@@ -298,11 +298,11 @@ protected: | |||
*/ | |||
void stopVoice (MPESynthesiserVoice* voice, MPENote noteToStop, bool allowTailOff); | |||
//========================================================================== | |||
//============================================================================== | |||
OwnedArray<MPESynthesiserVoice> voices; | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
bool shouldStealVoices; | |||
CriticalSection voicesLock; | |||
@@ -47,7 +47,7 @@ | |||
struct JUCE_API MPESynthesiserBase : public MPEInstrument::Listener | |||
{ | |||
public: | |||
//========================================================================== | |||
//============================================================================== | |||
/** Constructor. */ | |||
MPESynthesiserBase(); | |||
@@ -61,7 +61,7 @@ public: | |||
*/ | |||
MPESynthesiserBase (MPEInstrument* instrument); | |||
//========================================================================== | |||
//============================================================================== | |||
/** Returns the synthesiser's internal MPE zone layout. | |||
This happens by value, to enforce thread-safety and class invariants. | |||
*/ | |||
@@ -73,7 +73,7 @@ public: | |||
*/ | |||
void setZoneLayout (MPEZoneLayout newLayout); | |||
//========================================================================== | |||
//============================================================================== | |||
/** Tells the synthesiser what the sample rate is for the audio it's being | |||
used to render. | |||
*/ | |||
@@ -84,7 +84,7 @@ public: | |||
*/ | |||
double getSampleRate() const noexcept { return sampleRate; } | |||
//========================================================================== | |||
//============================================================================== | |||
/** Creates the next block of audio output. | |||
Call this to make sound. This will chop up the AudioBuffer into subBlock | |||
@@ -99,7 +99,7 @@ public: | |||
int startSample, | |||
int numSamples); | |||
//========================================================================== | |||
//============================================================================== | |||
/** Handle incoming MIDI events (called from renderNextBlock). | |||
The default implementation provided here simply forwards everything | |||
@@ -113,7 +113,7 @@ public: | |||
*/ | |||
virtual void handleMidiEvent (const MidiMessage&); | |||
//========================================================================== | |||
//============================================================================== | |||
/** Sets a minimum limit on the size to which audio sub-blocks will be divided when rendering. | |||
When rendering, the audio blocks that are passed into renderNextBlock() will be split up | |||
@@ -130,7 +130,7 @@ public: | |||
*/ | |||
void setMinimumRenderingSubdivisionSize (int numSamples) noexcept; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Puts the synthesiser into legacy mode. | |||
@param pitchbendRange The note pitchbend range in semitones to use when in legacy mode. | |||
@@ -160,7 +160,7 @@ public: | |||
void setLegacyModePitchbendRange (int pitchbendRange); | |||
protected: | |||
//========================================================================== | |||
//============================================================================== | |||
/** Implement this method to render your audio inside. | |||
@see renderNextBlock | |||
*/ | |||
@@ -176,14 +176,14 @@ protected: | |||
int /*numSamples*/) {} | |||
protected: | |||
//========================================================================== | |||
//============================================================================== | |||
/** @internal */ | |||
ScopedPointer<MPEInstrument> instrument; | |||
/** @internal */ | |||
CriticalSection renderAudioLock; | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
double sampleRate; | |||
int minimumSubBlockSize; | |||
@@ -37,7 +37,7 @@ | |||
class JUCE_API MPESynthesiserVoice | |||
{ | |||
public: | |||
//======================================================================== | |||
//============================================================================== | |||
/** Constructor. */ | |||
MPESynthesiserVoice(); | |||
@@ -160,7 +160,7 @@ public: | |||
bool wasStartedBefore (const MPESynthesiserVoice& other) const noexcept; | |||
protected: | |||
//========================================================================== | |||
//============================================================================== | |||
/** Resets the state of this voice after a sound has finished playing. | |||
The subclass must call this when it finishes playing a note and becomes available | |||
@@ -175,12 +175,12 @@ protected: | |||
*/ | |||
void clearCurrentNote() noexcept; | |||
//========================================================================== | |||
//============================================================================== | |||
double currentSampleRate; | |||
MPENote currentlyPlayingNote; | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
friend class MPESynthesiser; | |||
uint32 noteStartTime; | |||
@@ -144,7 +144,7 @@ public: | |||
} | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
void expectValuesConsistent (MPEValue value, | |||
int expectedValueAs7BitInt, | |||
int expectedValueAs14BitInt, | |||
@@ -157,7 +157,7 @@ private: | |||
expectFloatWithinRelativeError (value.asUnsignedFloat(), expectedValueAsUnsignedFloat, 0.0001f); | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
void expectFloatWithinRelativeError (float actualValue, float expectedValue, float maxRelativeError) | |||
{ | |||
const float maxAbsoluteError = jmax (1.0f, std::fabs (expectedValue)) * maxRelativeError; | |||
@@ -37,7 +37,7 @@ | |||
class JUCE_API MPEValue | |||
{ | |||
public: | |||
//========================================================================== | |||
//============================================================================== | |||
/** Default constructor. Constructs an MPEValue corresponding | |||
to the centre value. | |||
*/ | |||
@@ -87,7 +87,7 @@ public: | |||
bool operator!= (const MPEValue& other) const noexcept; | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
MPEValue (int normalisedValue); | |||
int normalisedValue; | |||
}; | |||
@@ -144,7 +144,7 @@ bool MPEZone::truncateToFit (MPEZone other) noexcept | |||
return true; | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
bool MPEZone::operator== (const MPEZone& other) const noexcept | |||
{ | |||
return masterChannel == other.masterChannel | |||
@@ -284,7 +284,7 @@ public: | |||
} | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
void testOverlapsWith (int masterChannelFirst, int numNoteChannelsFirst, | |||
int masterChannelSecond, int numNoteChannelsSecond, | |||
bool expectedRetVal) | |||
@@ -296,7 +296,7 @@ private: | |||
expect (second.overlapsWith (first) == expectedRetVal); | |||
} | |||
//========================================================================== | |||
//============================================================================== | |||
void testTruncateToFit (int masterChannelFirst, int numNoteChannelsFirst, | |||
int masterChannelSecond, int numNoteChannelsSecond, | |||
bool expectedRetVal, | |||
@@ -127,7 +127,7 @@ struct JUCE_API MPEZone | |||
bool operator!= (const MPEZone& other) const noexcept; | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
int masterChannel; | |||
int numNoteChannels; | |||
int perNotePitchbendRange; | |||
@@ -197,14 +197,6 @@ void MPEZoneLayout::removeListener (Listener* const listenerToRemove) noexcept | |||
listeners.remove (listenerToRemove); | |||
} | |||
MPEZoneLayout::Listener::Listener() | |||
{ | |||
} | |||
MPEZoneLayout::Listener::~Listener() | |||
{ | |||
} | |||
//============================================================================== | |||
//============================================================================== | |||
#if JUCE_UNIT_TESTS | |||
@@ -125,18 +125,15 @@ public: | |||
*/ | |||
MPEZone* getZoneByNoteChannel (int midiChannel) const noexcept; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Listener class. Derive from this class to allow your class to be | |||
notified about changes to the zone layout. | |||
*/ | |||
class Listener | |||
{ | |||
public: | |||
/** Constructor. */ | |||
Listener(); | |||
/** Destructor. */ | |||
virtual ~Listener(); | |||
virtual ~Listener() {} | |||
/** Implement this callback to be notified about any changes to this | |||
MPEZoneLayout. Will be called whenever a zone is added, zones are | |||
@@ -145,7 +142,7 @@ public: | |||
virtual void zoneLayoutChanged (const MPEZoneLayout& layout) = 0; | |||
}; | |||
//========================================================================== | |||
//============================================================================== | |||
/** Adds a listener. */ | |||
void addListener (Listener* const listenerToAdd) noexcept; | |||
@@ -153,7 +150,7 @@ public: | |||
void removeListener (Listener* const listenerToRemove) noexcept; | |||
private: | |||
//========================================================================== | |||
//============================================================================== | |||
Array<MPEZone> zones; | |||
MidiRPNDetector rpnDetector; | |||
ListenerList<Listener> listeners; | |||
@@ -46,7 +46,7 @@ public: | |||
@param source the input source to read from | |||
@param backgroundThread a background thread that will be used for the | |||
background read-ahead. This object must not be deleted | |||
until after any BufferedAudioSources that are using it | |||
until after any BufferingAudioSources that are using it | |||
have been deleted! | |||
@param deleteSourceWhenDeleted if true, then the input source object will | |||
be deleted when this object is deleted | |||
@@ -993,9 +993,9 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName) | |||
class AudioSampleBufferSource : public PositionableAudioSource | |||
{ | |||
public: | |||
AudioSampleBufferSource (AudioSampleBuffer* audioBuffer, bool ownBuffer) | |||
AudioSampleBufferSource (AudioSampleBuffer* audioBuffer, bool ownBuffer, bool playOnAllChannels) | |||
: buffer (audioBuffer, ownBuffer), | |||
position (0), looping (false) | |||
position (0), looping (false), playAcrossAllChannels (playOnAllChannels) | |||
{} | |||
//============================================================================== | |||
@@ -1029,8 +1029,11 @@ public: | |||
if (samplesToCopy > 0) | |||
{ | |||
const int maxInChannels = buffer->getNumChannels(); | |||
const int maxOutChannels = jmin (bufferToFill.buffer->getNumChannels(), jmax (maxInChannels, 2)); | |||
int maxInChannels = buffer->getNumChannels(); | |||
int maxOutChannels = bufferToFill.buffer->getNumChannels(); | |||
if (! playAcrossAllChannels) | |||
maxOutChannels = jmin (maxOutChannels, maxInChannels); | |||
for (int i = 0; i < maxOutChannels; ++i) | |||
bufferToFill.buffer->copyFrom (i, bufferToFill.startSample, *buffer, | |||
@@ -1047,7 +1050,7 @@ private: | |||
//============================================================================== | |||
OptionalScopedPointer<AudioSampleBuffer> buffer; | |||
int position; | |||
bool looping; | |||
bool looping, playAcrossAllChannels; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSampleBufferSource) | |||
}; | |||
@@ -1080,10 +1083,10 @@ void AudioDeviceManager::playSound (AudioFormatReader* reader, bool deleteWhenFi | |||
playSound (new AudioFormatReaderSource (reader, deleteWhenFinished), true); | |||
} | |||
void AudioDeviceManager::playSound (AudioSampleBuffer* buffer, bool deleteWhenFinished) | |||
void AudioDeviceManager::playSound (AudioSampleBuffer* buffer, bool deleteWhenFinished, bool playOnAllOutputChannels) | |||
{ | |||
if (buffer != nullptr) | |||
playSound (new AudioSampleBufferSource (buffer, deleteWhenFinished), true); | |||
playSound (new AudioSampleBufferSource (buffer, deleteWhenFinished, playOnAllOutputChannels), true); | |||
} | |||
void AudioDeviceManager::playSound (PositionableAudioSource* audioSource, bool deleteWhenFinished) | |||
@@ -1134,7 +1137,7 @@ void AudioDeviceManager::playTestSound() | |||
newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f); | |||
newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f); | |||
playSound (newSound, true); | |||
playSound (newSound, true, true); | |||
} | |||
//============================================================================== | |||
@@ -445,8 +445,15 @@ public: | |||
This will output the sound contained in an audio sample buffer. If | |||
deleteWhenFinished is true then the audio sample buffer will be | |||
automatically deleted once the sound has finished playing. | |||
If playOnAllOutputChannels is true, then if there are more output channels | |||
than buffer channels, then the ones that are available will be re-used on | |||
multiple outputs so that something is sent to all output channels. If it | |||
is false, then the buffer will just be played on the first output channels. | |||
*/ | |||
void playSound (AudioSampleBuffer* buffer, bool deleteWhenFinished = false); | |||
void playSound (AudioSampleBuffer* buffer, | |||
bool deleteWhenFinished = false, | |||
bool playOnAllOutputChannels = false); | |||
//============================================================================== | |||
/** Turns on level-measuring. | |||
@@ -31,7 +31,12 @@ | |||
#error "Incorrect use of JUCE cpp file" | |||
#endif | |||
#include "../juce_core/native/juce_BasicNativeHeaders.h" | |||
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1 | |||
#define JUCE_CORE_INCLUDE_COM_SMART_PTR 1 | |||
#define JUCE_CORE_INCLUDE_JNI_HELPERS 1 | |||
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1 | |||
#define JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW 1 | |||
#include "juce_audio_devices.h" | |||
//============================================================================== | |||
@@ -141,7 +146,6 @@ namespace juce | |||
//============================================================================== | |||
#if JUCE_MAC | |||
#include "../juce_core/native/juce_osx_ObjCHelpers.h" | |||
#include "native/juce_mac_CoreAudio.cpp" | |||
#include "native/juce_mac_CoreMidi.cpp" | |||
@@ -160,8 +164,6 @@ namespace juce | |||
//============================================================================== | |||
#elif JUCE_WINDOWS | |||
#include "../juce_core/native/juce_win32_ComSmartPtr.h" | |||
#include "../juce_events/native/juce_win32_HiddenMessageWindow.h" | |||
#if JUCE_WASAPI | |||
#include "native/juce_win32_WASAPI.cpp" | |||
@@ -203,7 +205,6 @@ namespace juce | |||
//============================================================================== | |||
#elif JUCE_ANDROID | |||
#include "../juce_core/native/juce_android_JNIHelpers.h" | |||
#include "native/juce_android_Audio.cpp" | |||
#include "native/juce_android_Midi.cpp" | |||
@@ -29,7 +29,7 @@ | |||
#include "../juce_audio_basics/juce_audio_basics.h" | |||
#include "../juce_audio_formats/juce_audio_formats.h" | |||
//============================================================================= | |||
//============================================================================== | |||
/** Config: JUCE_ASIO | |||
Enables ASIO audio devices (MS Windows only). | |||
Turning this on means that you'll need to have the Steinberg ASIO SDK installed | |||
@@ -90,7 +90,7 @@ | |||
#endif | |||
#endif | |||
//============================================================================= | |||
//============================================================================== | |||
/** Config: JUCE_USE_CDREADER | |||
Enables the AudioCDReader class (on supported platforms). | |||
*/ | |||
@@ -105,7 +105,7 @@ | |||
#define JUCE_USE_CDBURNER 0 | |||
#endif | |||
//============================================================================= | |||
//============================================================================== | |||
namespace juce | |||
{ | |||
@@ -195,25 +195,51 @@ public: | |||
STREAM_MUSIC, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT, | |||
(jint) (minBufferSizeOut * numDeviceOutputChannels * sizeof (int16)), MODE_STREAM)); | |||
if (env->CallIntMethod (outputDevice, AudioTrack.getState) != STATE_UNINITIALIZED) | |||
int outputDeviceState = env->CallIntMethod (outputDevice, AudioTrack.getState); | |||
if (outputDeviceState > 0) | |||
{ | |||
isRunning = true; | |||
} | |||
else | |||
outputDevice.clear(); // failed to open the device | |||
{ | |||
// failed to open the device | |||
outputDevice.clear(); | |||
lastError = "Error opening audio output device: android.media.AudioTrack failed with state = " + String (outputDeviceState); | |||
} | |||
} | |||
if (numClientInputChannels > 0 && numDeviceInputChannelsAvailable > 0) | |||
{ | |||
numDeviceInputChannels = jmin (numClientInputChannels, numDeviceInputChannelsAvailable); | |||
inputDevice = GlobalRef (env->NewObject (AudioRecord, AudioRecord.constructor, | |||
0 /* (default audio source) */, sampleRate, | |||
numDeviceInputChannelsAvailable > 1 ? CHANNEL_IN_STEREO : CHANNEL_IN_MONO, | |||
ENCODING_PCM_16BIT, | |||
(jint) (minBufferSizeIn * numDeviceInputChannels * sizeof (int16)))); | |||
if (env->CallIntMethod (inputDevice, AudioRecord.getState) != STATE_UNINITIALIZED) | |||
isRunning = true; | |||
if (! RuntimePermissions::isGranted (RuntimePermissions::recordAudio)) | |||
{ | |||
// If you hit this assert, you probably forgot to get RuntimePermissions::recordAudio | |||
// before trying to open an audio input device. This is not going to work! | |||
jassertfalse; | |||
inputDevice.clear(); | |||
lastError = "Error opening audio input device: the app was not granted android.permission.RECORD_AUDIO"; | |||
} | |||
else | |||
inputDevice.clear(); // failed to open the device | |||
{ | |||
numDeviceInputChannels = jmin (numClientInputChannels, numDeviceInputChannelsAvailable); | |||
inputDevice = GlobalRef (env->NewObject (AudioRecord, AudioRecord.constructor, | |||
0 /* (default audio source) */, sampleRate, | |||
numDeviceInputChannelsAvailable > 1 ? CHANNEL_IN_STEREO : CHANNEL_IN_MONO, | |||
ENCODING_PCM_16BIT, | |||
(jint) (minBufferSizeIn * numDeviceInputChannels * sizeof (int16)))); | |||
int inputDeviceState = env->CallIntMethod (inputDevice, AudioRecord.getState); | |||
if (inputDeviceState > 0) | |||
{ | |||
isRunning = true; | |||
} | |||
else | |||
{ | |||
// failed to open the device | |||
inputDevice.clear(); | |||
lastError = "Error opening audio input device: android.media.AudioRecord failed with state = " + String (inputDeviceState); | |||
} | |||
} | |||
} | |||
if (isRunning) | |||
@@ -368,7 +394,7 @@ public: | |||
int minBufferSizeOut, minBufferSizeIn; | |||
private: | |||
//================================================================================================== | |||
//============================================================================== | |||
CriticalSection callbackLock; | |||
AudioIODeviceCallback* callback; | |||
jint sampleRate; | |||
@@ -145,12 +145,32 @@ public: | |||
<< ", sampleRate = " << sampleRate); | |||
if (numInputChannels > 0) | |||
recorder = engine.createRecorder (numInputChannels, sampleRate, | |||
audioBuffersToEnqueue, actualBufferSize); | |||
{ | |||
if (! RuntimePermissions::isGranted (RuntimePermissions::recordAudio)) | |||
{ | |||
// If you hit this assert, you probably forgot to get RuntimePermissions::recordAudio | |||
// before trying to open an audio input device. This is not going to work! | |||
jassertfalse; | |||
lastError = "Error opening OpenSL input device: the app was not granted android.permission.RECORD_AUDIO"; | |||
} | |||
else | |||
{ | |||
recorder = engine.createRecorder (numInputChannels, sampleRate, | |||
audioBuffersToEnqueue, actualBufferSize); | |||
if (recorder == nullptr) | |||
lastError = "Error opening OpenSL input device: creating Recorder failed."; | |||
} | |||
} | |||
if (numOutputChannels > 0) | |||
player = engine.createPlayer (numOutputChannels, sampleRate, | |||
audioBuffersToEnqueue, actualBufferSize); | |||
{ | |||
player = engine.createPlayer (numOutputChannels, sampleRate, | |||
audioBuffersToEnqueue, actualBufferSize); | |||
if (player == nullptr) | |||
lastError = "Error opening OpenSL input device: creating Player failed."; | |||
} | |||
// pre-fill buffers | |||
for (int i = 0; i < audioBuffersToEnqueue; ++i) | |||
@@ -220,7 +240,7 @@ public: | |||
} | |||
private: | |||
//================================================================================================== | |||
//============================================================================== | |||
CriticalSection callbackLock; | |||
AudioIODeviceCallback* callback; | |||
int actualBufferSize, sampleRate; | |||
@@ -242,7 +262,7 @@ private: | |||
defaultBufferSizeIsMultipleOfNative = 1 | |||
}; | |||
//================================================================================================== | |||
//============================================================================== | |||
static String audioManagerGetProperty (const String& property) | |||
{ | |||
const LocalRef<jstring> jProperty (javaString (property)); | |||
@@ -281,7 +301,7 @@ private: | |||
return androidHasSystemFeature ("android.hardware.audio.low_latency"); | |||
} | |||
//================================================================================================== | |||
//============================================================================== | |||
AudioIODeviceCallback* setCallback (AudioIODeviceCallback* const newCallback) | |||
{ | |||
const ScopedLock sl (callbackLock); | |||
@@ -331,7 +351,7 @@ private: | |||
DBG ("Unable to set audio thread priority: priority is still " << priority); | |||
} | |||
//================================================================================================== | |||
//============================================================================== | |||
struct Engine | |||
{ | |||
Engine() | |||
@@ -400,7 +420,7 @@ private: | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Engine) | |||
}; | |||
//================================================================================================== | |||
//============================================================================== | |||
struct BufferList | |||
{ | |||
BufferList (const int numChannels_, const int numBuffers_, const int numSamples_) | |||
@@ -444,7 +464,7 @@ private: | |||