Browse Source

Update juce

tags/1.9.7
falkTX 9 years ago
parent
commit
5a018dc77f
100 changed files with 2076 additions and 871 deletions
  1. +9
    -22
      source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp
  2. +6
    -0
      source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h
  3. +63
    -0
      source/modules/juce_audio_basics/effects/juce_CatmullRomInterpolator.cpp
  4. +89
    -0
      source/modules/juce_audio_basics/effects/juce_CatmullRomInterpolator.h
  5. +133
    -139
      source/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.cpp
  6. +4
    -9
      source/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.h
  7. +4
    -4
      source/modules/juce_audio_basics/effects/juce_LinearSmoothedValue.h
  8. +1
    -2
      source/modules/juce_audio_basics/juce_audio_basics.cpp
  9. +2
    -1
      source/modules/juce_audio_basics/juce_audio_basics.h
  10. +1
    -1
      source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp
  11. +16
    -5
      source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp
  12. +10
    -1
      source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h
  13. +2
    -2
      source/modules/juce_audio_basics/midi/juce_MidiRPN.cpp
  14. +6
    -6
      source/modules/juce_audio_basics/midi/juce_MidiRPN.h
  15. +5
    -13
      source/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp
  16. +14
    -17
      source/modules/juce_audio_basics/mpe/juce_MPEInstrument.h
  17. +2
    -2
      source/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp
  18. +2
    -2
      source/modules/juce_audio_basics/mpe/juce_MPENote.cpp
  19. +6
    -6
      source/modules/juce_audio_basics/mpe/juce_MPENote.h
  20. +9
    -9
      source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h
  21. +10
    -10
      source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h
  22. +4
    -4
      source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.h
  23. +2
    -2
      source/modules/juce_audio_basics/mpe/juce_MPEValue.cpp
  24. +2
    -2
      source/modules/juce_audio_basics/mpe/juce_MPEValue.h
  25. +3
    -3
      source/modules/juce_audio_basics/mpe/juce_MPEZone.cpp
  26. +1
    -1
      source/modules/juce_audio_basics/mpe/juce_MPEZone.h
  27. +0
    -8
      source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp
  28. +4
    -7
      source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h
  29. +1
    -1
      source/modules/juce_audio_basics/sources/juce_BufferingAudioSource.h
  30. +11
    -8
      source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp
  31. +8
    -1
      source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h
  32. +6
    -5
      source/modules/juce_audio_devices/juce_audio_devices.cpp
  33. +3
    -3
      source/modules/juce_audio_devices/juce_audio_devices.h
  34. +39
    -13
      source/modules/juce_audio_devices/native/juce_android_Audio.cpp
  35. +31
    -11
      source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp
  36. +27
    -22
      source/modules/juce_audio_devices/native/juce_ios_Audio.cpp
  37. +64
    -38
      source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp
  38. +36
    -27
      source/modules/juce_audio_devices/native/juce_win32_ASIO.cpp
  39. +5
    -0
      source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp
  40. +1
    -1
      source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/metadata.h
  41. +18
    -9
      source/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp
  42. +0
    -4
      source/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.cpp
  43. +4
    -10
      source/modules/juce_audio_formats/juce_audio_formats.cpp
  44. +2
    -2
      source/modules/juce_audio_formats/juce_audio_formats.h
  45. +0
    -5
      source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm
  46. +0
    -1
      source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp
  47. +39
    -39
      source/modules/juce_audio_processors/format_types/juce_VST3Common.h
  48. +0
    -8
      source/modules/juce_audio_processors/format_types/juce_VST3Headers.h
  49. +3
    -11
      source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp
  50. +5
    -10
      source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp
  51. +2
    -5
      source/modules/juce_audio_processors/juce_audio_processors.cpp
  52. +3
    -3
      source/modules/juce_audio_processors/juce_audio_processors.h
  53. +18
    -12
      source/modules/juce_audio_processors/processors/juce_AudioChannelSet.cpp
  54. +2
    -2
      source/modules/juce_audio_processors/processors/juce_AudioChannelSet.h
  55. +14
    -12
      source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp
  56. +16
    -8
      source/modules/juce_audio_processors/processors/juce_AudioProcessor.h
  57. +0
    -6
      source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp
  58. +0
    -2
      source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h
  59. +14
    -17
      source/modules/juce_audio_processors/utilities/juce_AudioProcessorParameters.cpp
  60. +6
    -6
      source/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.cpp
  61. +7
    -0
      source/modules/juce_core/containers/juce_HashMap.h
  62. +62
    -22
      source/modules/juce_core/files/juce_File.cpp
  63. +1
    -1
      source/modules/juce_core/files/juce_File.h
  64. +2
    -2
      source/modules/juce_core/files/juce_WildcardFileFilter.h
  65. +57
    -8
      source/modules/juce_core/javascript/juce_Javascript.cpp
  66. +7
    -6
      source/modules/juce_core/juce_core.cpp
  67. +25
    -6
      source/modules/juce_core/juce_core.h
  68. +1
    -1
      source/modules/juce_core/maths/juce_BigInteger.cpp
  69. +10
    -6
      source/modules/juce_core/maths/juce_MathsFunctions.h
  70. +145
    -0
      source/modules/juce_core/maths/juce_StatisticsAccumulator.h
  71. +1
    -1
      source/modules/juce_core/memory/juce_Atomic.h
  72. +1
    -1
      source/modules/juce_core/memory/juce_ByteOrder.h
  73. +6
    -0
      source/modules/juce_core/memory/juce_OptionalScopedPointer.h
  74. +48
    -0
      source/modules/juce_core/misc/juce_RuntimePermissions.cpp
  75. +131
    -0
      source/modules/juce_core/misc/juce_RuntimePermissions.h
  76. +14
    -0
      source/modules/juce_core/native/java/AndroidRuntimePermissions.java
  77. +145
    -23
      source/modules/juce_core/native/java/JuceAppActivity.java
  78. +0
    -5
      source/modules/juce_core/native/juce_BasicNativeHeaders.h
  79. +11
    -7
      source/modules/juce_core/native/juce_android_Files.cpp
  80. +10
    -1
      source/modules/juce_core/native/juce_android_JNIHelpers.h
  81. +90
    -0
      source/modules/juce_core/native/juce_android_RuntimePermissions.cpp
  82. +7
    -8
      source/modules/juce_core/native/juce_android_Threads.cpp
  83. +1
    -1
      source/modules/juce_core/native/juce_linux_CommonFile.cpp
  84. +3
    -0
      source/modules/juce_core/native/juce_linux_SystemStats.cpp
  85. +8
    -2
      source/modules/juce_core/native/juce_mac_Strings.mm
  86. +5
    -15
      source/modules/juce_core/native/juce_mac_SystemStats.mm
  87. +61
    -7
      source/modules/juce_core/native/juce_posix_SharedCode.h
  88. +16
    -7
      source/modules/juce_core/native/juce_win32_Registry.cpp
  89. +8
    -2
      source/modules/juce_core/native/juce_win32_SystemStats.cpp
  90. +2
    -1
      source/modules/juce_core/native/juce_win32_Threads.cpp
  91. +41
    -43
      source/modules/juce_core/network/juce_Socket.cpp
  92. +12
    -3
      source/modules/juce_core/system/juce_PlatformDefs.h
  93. +6
    -2
      source/modules/juce_core/system/juce_SystemStats.cpp
  94. +10
    -7
      source/modules/juce_core/system/juce_SystemStats.h
  95. +1
    -6
      source/modules/juce_core/system/juce_TargetPlatform.h
  96. +3
    -0
      source/modules/juce_core/text/juce_CharPointer_UTF8.h
  97. +9
    -0
      source/modules/juce_core/threads/juce_Process.h
  98. +2
    -1
      source/modules/juce_core/threads/juce_Thread.cpp
  99. +8
    -1
      source/modules/juce_core/threads/juce_Thread.h
  100. +311
    -95
      source/modules/juce_core/time/juce_Time.cpp

+ 9
- 22
source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp View File

@@ -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


+ 6
- 0
source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h View File

@@ -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;
};


+ 63
- 0
source/modules/juce_audio_basics/effects/juce_CatmullRomInterpolator.cpp View File

@@ -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);
}

+ 89
- 0
source/modules/juce_audio_basics/effects/juce_CatmullRomInterpolator.h View File

@@ -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)
};

+ 133
- 139
source/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.cpp View File

@@ -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);
}

+ 4
- 9
source/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.h View File

@@ -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

+ 4
- 4
source/modules/juce_audio_basics/effects/juce_LinearSmoothedValue.h View File

@@ -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;
};


+ 1
- 2
source/modules/juce_audio_basics/juce_audio_basics.cpp View File

@@ -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"


+ 2
- 1
source/modules/juce_audio_basics/juce_audio_basics.h View File

@@ -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"


+ 1
- 1
source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp View File

@@ -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);
}


+ 16
- 5
source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp View File

@@ -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);
}


+ 10
- 1
source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h View File

@@ -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.


+ 2
- 2
source/modules/juce_audio_basics/midi/juce_MidiRPN.cpp View File

@@ -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);


+ 6
- 6
source/modules/juce_audio_basics/midi/juce_MidiRPN.h View File

@@ -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.


+ 5
- 13
source/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp View File

@@ -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;
};


+ 14
- 17
source/modules/juce_audio_basics/mpe/juce_MPEInstrument.h View File

@@ -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;


+ 2
- 2
source/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp View File

@@ -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;


+ 2
- 2
source/modules/juce_audio_basics/mpe/juce_MPENote.cpp View File

@@ -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)
{


+ 6
- 6
source/modules/juce_audio_basics/mpe/juce_MPENote.h View File

@@ -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.
*/


+ 9
- 9
source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h View File

@@ -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;


+ 10
- 10
source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h View File

@@ -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;


+ 4
- 4
source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.h View File

@@ -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;


+ 2
- 2
source/modules/juce_audio_basics/mpe/juce_MPEValue.cpp View File

@@ -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;


+ 2
- 2
source/modules/juce_audio_basics/mpe/juce_MPEValue.h View File

@@ -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;
};


+ 3
- 3
source/modules/juce_audio_basics/mpe/juce_MPEZone.cpp View File

@@ -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,


+ 1
- 1
source/modules/juce_audio_basics/mpe/juce_MPEZone.h View File

@@ -127,7 +127,7 @@ struct JUCE_API MPEZone
bool operator!= (const MPEZone& other) const noexcept;
private:
//==========================================================================
//==============================================================================
int masterChannel;
int numNoteChannels;
int perNotePitchbendRange;


+ 0
- 8
source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp View File

@@ -197,14 +197,6 @@ void MPEZoneLayout::removeListener (Listener* const listenerToRemove) noexcept
listeners.remove (listenerToRemove);
}
MPEZoneLayout::Listener::Listener()
{
}
MPEZoneLayout::Listener::~Listener()
{
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS


+ 4
- 7
source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h View File

@@ -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;


+ 1
- 1
source/modules/juce_audio_basics/sources/juce_BufferingAudioSource.h View File

@@ -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


+ 11
- 8
source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp View File

@@ -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);
}
//==============================================================================


+ 8
- 1
source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h View File

@@ -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.


+ 6
- 5
source/modules/juce_audio_devices/juce_audio_devices.cpp View File

@@ -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"


+ 3
- 3
source/modules/juce_audio_devices/juce_audio_devices.h View File

@@ -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
{


+ 39
- 13
source/modules/juce_audio_devices/native/juce_android_Audio.cpp View File

@@ -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;


+ 31
- 11
source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp View File

@@ -145,12 +145,32 @@ public:
<< ", sampleRate = " << sampleRate);
if (numInputChannels > 0)
recorder = engine.createRecorder (numInputChannels, sampleRate,
audioBuffersToEnqueue, actualBufferSize);
{
if (! RuntimePermissions::isGranted (RuntimePermissions::recordAudio))
{
// If you hit this assert, you probably forgot to get RuntimePermissions::recordAudio
// before trying to open an audio input device. This is not going to work!
jassertfalse;
lastError = "Error opening OpenSL input device: the app was not granted android.permission.RECORD_AUDIO";
}
else
{
recorder = engine.createRecorder (numInputChannels, sampleRate,
audioBuffersToEnqueue, actualBufferSize);
if (recorder == nullptr)
lastError = "Error opening OpenSL input device: creating Recorder failed.";
}
}
if (numOutputChannels > 0)
player = engine.createPlayer (numOutputChannels, sampleRate,
audioBuffersToEnqueue, actualBufferSize);
{
player = engine.createPlayer (numOutputChannels, sampleRate,
audioBuffersToEnqueue, actualBufferSize);
if (player == nullptr)
lastError = "Error opening OpenSL input device: creating Player failed.";
}
// pre-fill buffers
for (int i = 0; i < audioBuffersToEnqueue; ++i)
@@ -220,7 +240,7 @@ public:
}
private:
//==================================================================================================
//==============================================================================
CriticalSection callbackLock;
AudioIODeviceCallback* callback;
int actualBufferSize, sampleRate;
@@ -242,7 +262,7 @@ private:
defaultBufferSizeIsMultipleOfNative = 1
};
//==================================================================================================
//==============================================================================
static String audioManagerGetProperty (const String& property)
{
const LocalRef<jstring> jProperty (javaString (property));
@@ -281,7 +301,7 @@ private:
return androidHasSystemFeature ("android.hardware.audio.low_latency");
}
//==================================================================================================
//==============================================================================
AudioIODeviceCallback* setCallback (AudioIODeviceCallback* const newCallback)
{
const ScopedLock sl (callbackLock);
@@ -331,7 +351,7 @@ private:
DBG ("Unable to set audio thread priority: priority is still " << priority);
}
//==================================================================================================
//==============================================================================
struct Engine
{
Engine()
@@ -400,7 +420,7 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Engine)
};
//==================================================================================================
//==============================================================================
struct BufferList
{
BufferList (const int numChannels_, const int numBuffers_, const int numSamples_)
@@ -444,7 +464,7 @@ private:
WaitableEvent dataArrived;
};
//==================================================================================================
//==============================================================================
struct Player
{
Player (int numChannels, int sampleRate, Engine& engine, int playerNumBuffers, int playerBufferSize)
@@ -559,7 +579,7 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Player)
};
//==================================================================================================
//==============================================================================
struct Recorder
{
Recorder (int numChannels, int sampleRate, Engine& engine, const int numBuffers, const int numSamples)


+ 27
- 22
source/modules/juce_audio_devices/native/juce_ios_Audio.cpp View File

@@ -24,7 +24,9 @@
class iOSAudioIODevice;
//==================================================================================================
static const char* const iOSAudioDeviceName = "iOS Audio";
//==============================================================================
struct AudioSessionHolder
{
AudioSessionHolder();
@@ -74,7 +76,7 @@ bool getNotificationValueForKey (NSNotification* notification, NSString* key, NS
} // juce namespace
//==================================================================================================
//==============================================================================
@interface iOSAudioSessionNative : NSObject
{
@private
@@ -178,7 +180,7 @@ bool getNotificationValueForKey (NSNotification* notification, NSString* key, NS
@end
//==================================================================================================
//==============================================================================
namespace juce {
#ifndef JUCE_IOS_AUDIO_LOGGING
@@ -203,11 +205,12 @@ static void logNSError (NSError* e)
#define JUCE_NSERROR_CHECK(X) { NSError* error = nil; X; logNSError (error); }
//==================================================================================================
//==============================================================================
class iOSAudioIODevice : public AudioIODevice
{
public:
iOSAudioIODevice (const String& deviceName) : AudioIODevice (deviceName, "Audio")
iOSAudioIODevice (const String& deviceName)
: AudioIODevice (deviceName, iOSAudioDeviceName)
{
sessionHolder->activeDevices.add (this);
updateSampleRateAndAudioInput();
@@ -350,13 +353,14 @@ public:
{
isRunning = false;
setAudioSessionActive (false);
if (audioUnit != 0)
{
AudioOutputUnitStart (audioUnit);
AudioComponentInstanceDispose (audioUnit);
audioUnit = 0;
}
setAudioSessionActive (false);
}
}
@@ -463,13 +467,13 @@ public:
AudioOutputUnitStart (audioUnit);
}
if (callback)
if (callback != nullptr)
callback->audioDeviceAboutToStart (this);
}
}
private:
//==================================================================================================
//==============================================================================
SharedResourcePointer<AudioSessionHolder> sessionHolder;
CriticalSection callbackLock;
NSTimeInterval sampleRate = 0;
@@ -505,7 +509,7 @@ private:
}
}
//==================================================================================================
//==============================================================================
OSStatus process (AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time,
const UInt32 numFrames, AudioBufferList* data)
{
@@ -592,21 +596,22 @@ private:
auto session = [AVAudioSession sharedInstance];
sampleRate = session.sampleRate;
audioInputIsAvailable = session.isInputAvailable;
JUCE_IOS_AUDIO_LOG ("AVAudioSession: sampleRate: " << sampleRate << "Hz, audioInputAvailable: " << (int) audioInputIsAvailable);
actualBufferSize = roundToInt (sampleRate * session.IOBufferDuration);
JUCE_IOS_AUDIO_LOG ("AVAudioSession: sampleRate: " << sampleRate
<< "Hz, audioInputAvailable: " << (int) audioInputIsAvailable);
}
void updateCurrentBufferSize()
{
auto session = [AVAudioSession sharedInstance];
NSTimeInterval bufferDuration = sampleRate > 0 ? (NSTimeInterval) (preferredBufferSize / sampleRate) : 0.0;
JUCE_NSERROR_CHECK ([session setPreferredIOBufferDuration: bufferDuration
error: &error]);
NSTimeInterval bufferDuration = sampleRate > 0 ? (NSTimeInterval) ((preferredBufferSize + 1) / sampleRate) : 0.0;
bufferDuration = session.IOBufferDuration;
actualBufferSize = roundToInt (sampleRate * bufferDuration);
JUCE_NSERROR_CHECK ([[AVAudioSession sharedInstance] setPreferredIOBufferDuration: bufferDuration
error: &error]);
updateSampleRateAndAudioInput();
}
//==================================================================================================
//==============================================================================
static OSStatus processStatic (void* client, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time,
UInt32 /*busNumber*/, UInt32 numFrames, AudioBufferList* data)
{
@@ -614,7 +619,7 @@ private:
return static_cast<iOSAudioIODevice*> (client)->process (flags, time, numFrames, data);
}
//==================================================================================================
//==============================================================================
void resetFormat (const int numChannels) noexcept
{
zerostruct (format);
@@ -712,10 +717,10 @@ private:
class iOSAudioIODeviceType : public AudioIODeviceType
{
public:
iOSAudioIODeviceType() : AudioIODeviceType ("iOS Audio") {}
iOSAudioIODeviceType() : AudioIODeviceType (iOSAudioDeviceName) {}
void scanForDevices() {}
StringArray getDeviceNames (bool /*wantInputNames*/) const { return StringArray ("iOS Audio"); }
StringArray getDeviceNames (bool /*wantInputNames*/) const { return StringArray (iOSAudioDeviceName); }
int getDefaultDeviceIndex (bool /*forInput*/) const { return 0; }
int getIndexOfDevice (AudioIODevice* d, bool /*asInput*/) const { return d != nullptr ? 0 : -1; }
bool hasSeparateInputsAndOutputs() const { return false; }
@@ -738,7 +743,7 @@ AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio()
return new iOSAudioIODeviceType();
}
//==================================================================================================
//==============================================================================
AudioSessionHolder::AudioSessionHolder() { nativeSession = [[iOSAudioSessionNative alloc] init: this]; }
AudioSessionHolder::~AudioSessionHolder() { [nativeSession release]; }


+ 64
- 38
source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp View File

@@ -31,6 +31,12 @@
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnonnull" // aovid some spurious 10.11 SDK warnings
// The AudioHardwareService stuff was deprecated in 10.11 but there's no replacement yet,
// so we'll have to silence the warnings here and revisit it in a future OS version..
#if defined (MAC_OS_X_VERSION_10_11) && MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_11
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
#endif
//==============================================================================
@@ -382,65 +388,84 @@ public:
return 0;
}
void updateDetailsFromDevice()
int getFrameSizeFromDevice() const
{
stopTimer();
AudioObjectPropertyAddress pa;
pa.mScope = kAudioObjectPropertyScopeWildcard;
pa.mElement = kAudioObjectPropertyElementMaster;
pa.mSelector = kAudioDevicePropertyBufferFrameSize;
if (deviceID == 0)
return;
UInt32 framesPerBuf = (UInt32) bufferSize;
UInt32 size = sizeof (framesPerBuf);
AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &framesPerBuf);
return (int) framesPerBuf;
}
// this collects all the new details from the device without any locking, then
// locks + swaps them afterwards.
bool isDeviceAlive() const
{
AudioObjectPropertyAddress pa;
pa.mScope = kAudioObjectPropertyScopeWildcard;
pa.mElement = kAudioObjectPropertyElementMaster;
pa.mSelector = kAudioDevicePropertyDeviceIsAlive;
UInt32 isAlive;
UInt32 isAlive = 0;
UInt32 size = sizeof (isAlive);
pa.mSelector = kAudioDevicePropertyDeviceIsAlive;
if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &isAlive)) && isAlive == 0)
return;
return deviceID != 0
&& OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &isAlive))
&& isAlive != 0;
}
const double currentRate = getNominalSampleRate();
if (currentRate > 0)
sampleRate = currentRate;
bool updateDetailsFromDevice()
{
stopTimer();
UInt32 framesPerBuf = (UInt32) bufferSize;
size = sizeof (framesPerBuf);
pa.mSelector = kAudioDevicePropertyBufferFrameSize;
AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &framesPerBuf);
if (! isDeviceAlive())
return false;
// this collects all the new details from the device without any locking, then
// locks + swaps them afterwards.
Array<int> newBufferSizes (getBufferSizesFromDevice());
Array<double> newSampleRates (getSampleRatesFromDevice());
const double newSampleRate = getNominalSampleRate();
const int newBufferSize = getFrameSizeFromDevice();
inputLatency = getLatencyFromDevice (kAudioDevicePropertyScopeInput);
outputLatency = getLatencyFromDevice (kAudioDevicePropertyScopeOutput);
Array<int> newBufferSizes = getBufferSizesFromDevice();
Array<double> newSampleRates = getSampleRatesFromDevice();
const int newInputLatency = getLatencyFromDevice (kAudioDevicePropertyScopeInput);
const int newOutputLatency = getLatencyFromDevice (kAudioDevicePropertyScopeOutput);
Array<CallbackDetailsForChannel> newInChans, newOutChans;
StringArray newInNames (getChannelInfo (true, newInChans));
StringArray newOutNames (getChannelInfo (false, newOutChans));
const int inputBitDepth = getBitDepthFromDevice (kAudioDevicePropertyScopeInput);
const int outputBitDepth = getBitDepthFromDevice (kAudioDevicePropertyScopeOutput);
const int newBitDepth = jmax (getBitDepthFromDevice (kAudioDevicePropertyScopeInput),
getBitDepthFromDevice (kAudioDevicePropertyScopeOutput));
{
const ScopedLock sl (callbackLock);
bitDepth = jmax (inputBitDepth, outputBitDepth);
if (bitDepth <= 0)
bitDepth = 32;
bitDepth = newBitDepth > 0 ? newBitDepth : 32;
// after getting the new values, lock + apply them
const ScopedLock sl (callbackLock);
if (newSampleRate > 0)
sampleRate = newSampleRate;
bufferSize = (int) framesPerBuf;
allocateTempBuffers();
inputLatency = newInputLatency;
outputLatency = newOutputLatency;
bufferSize = newBufferSize;
sampleRates.swapWith (newSampleRates);
bufferSizes.swapWith (newBufferSizes);
sampleRates.swapWith (newSampleRates);
bufferSizes.swapWith (newBufferSizes);
inChanNames.swapWith (newInNames);
outChanNames.swapWith (newOutNames);
inChanNames.swapWith (newInNames);
outChanNames.swapWith (newOutNames);
inputChannelInfo.swapWith (newInChans);
outputChannelInfo.swapWith (newOutChans);
inputChannelInfo.swapWith (newInChans);
outputChannelInfo.swapWith (newOutChans);
allocateTempBuffers();
}
return true;
}
//==============================================================================
@@ -763,9 +788,10 @@ public:
stopTimer();
const double oldSampleRate = sampleRate;
const int oldBufferSize = bufferSize;
updateDetailsFromDevice();
if (oldBufferSize != bufferSize || oldSampleRate != sampleRate)
if (! updateDetailsFromDevice())
owner.stop();
else if (oldBufferSize != bufferSize || oldSampleRate != sampleRate)
owner.restart();
}


+ 36
- 27
source/modules/juce_audio_devices/native/juce_win32_ASIO.cpp View File

@@ -320,9 +320,9 @@ public:
classId (clsID),
inputLatency (0),
outputLatency (0),
minSize (0), maxSize (0),
preferredSize (0),
granularity (0),
minBufferSize (0), maxBufferSize (0),
preferredBufferSize (0),
bufferGranularity (0),
numClockSources (0),
currentBlockSizeSamples (0),
currentBitDepth (16),
@@ -403,7 +403,7 @@ public:
Array<double> getAvailableSampleRates() override { return sampleRates; }
Array<int> getAvailableBufferSizes() override { return bufferSizes; }
int getDefaultBufferSize() override { return preferredSize; }
int getDefaultBufferSize() override { return preferredBufferSize; }
String open (const BigInteger& inputChannels,
const BigInteger& outputChannels,
@@ -469,10 +469,10 @@ public:
removeCurrentDriver();
loadDriver();
const String error (initDriver());
String initError = initDriver();
if (error.isNotEmpty())
JUCE_ASIO_LOG ("ASIOInit: " + error);
if (initError.isNotEmpty())
JUCE_ASIO_LOG ("ASIOInit: " + initError);
needToReset = false;
}
@@ -489,7 +489,7 @@ public:
if (err != ASE_OK)
{
currentBlockSizeSamples = preferredSize;
currentBlockSizeSamples = preferredBufferSize;
JUCE_ASIO_LOG_ERROR ("create buffers 2", err);
asioObject->disposeBuffers();
@@ -561,8 +561,7 @@ public:
}
readLatencies();
asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity);
refreshBufferSizes();
deviceIsOpen = true;
JUCE_ASIO_LOG ("starting");
@@ -762,7 +761,7 @@ private:
Array<double> sampleRates;
Array<int> bufferSizes;
long inputLatency, outputLatency;
long minSize, maxSize, preferredSize, granularity;
long minBufferSize, maxBufferSize, preferredBufferSize, bufferGranularity;
ASIOClockSource clocks[32];
int numClockSources;
@@ -829,23 +828,27 @@ private:
}
}
int readBufferSizes (int bufferSizeSamples)
long refreshBufferSizes()
{
minSize = 0;
maxSize = 0;
granularity = 0;
return asioObject->getBufferSize (&minBufferSize, &maxBufferSize, &preferredBufferSize, &bufferGranularity);
}
int readBufferSizes (int bufferSizeSamples)
{
minBufferSize = 0;
maxBufferSize = 0;
bufferGranularity = 0;
long newPreferredSize = 0;
if (asioObject->getBufferSize (&minSize, &maxSize, &newPreferredSize, &granularity) == ASE_OK)
if (asioObject->getBufferSize (&minBufferSize, &maxBufferSize, &newPreferredSize, &bufferGranularity) == ASE_OK)
{
if (preferredSize != 0 && newPreferredSize != 0 && newPreferredSize != preferredSize)
if (preferredBufferSize != 0 && newPreferredSize != 0 && newPreferredSize != preferredBufferSize)
shouldUsePreferredSize = true;
if (bufferSizeSamples < minSize || bufferSizeSamples > maxSize)
if (bufferSizeSamples < minBufferSize || bufferSizeSamples > maxBufferSize)
shouldUsePreferredSize = true;
preferredSize = newPreferredSize;
preferredBufferSize = newPreferredSize;
}
// unfortunate workaround for certain drivers which crash if you make
@@ -855,11 +858,11 @@ private:
if (shouldUsePreferredSize)
{
JUCE_ASIO_LOG ("Using preferred size for buffer..");
long err = asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity);
long err = refreshBufferSizes();
if (err == ASE_OK)
{
bufferSizeSamples = (int) preferredSize;
bufferSizeSamples = (int) preferredBufferSize;
}
else
{
@@ -1082,8 +1085,8 @@ private:
if (i < 2)
{
// clear the channels that are used with the dummy stuff
outputFormat[i].clear (bufferInfos [outputBufferIndex + i].buffers[0], preferredSize);
outputFormat[i].clear (bufferInfos [outputBufferIndex + i].buffers[1], preferredSize);
outputFormat[i].clear (bufferInfos [outputBufferIndex + i].buffers[0], preferredBufferSize);
outputFormat[i].clear (bufferInfos [outputBufferIndex + i].buffers[1], preferredBufferSize);
}
}
}
@@ -1203,9 +1206,9 @@ private:
inputFormat.calloc (chansToAllocate);
outputFormat.calloc (chansToAllocate);
if ((err = asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity)) == 0)
if ((err = refreshBufferSizes()) == 0)
{
addBufferSizes (minSize, maxSize, preferredSize, granularity);
addBufferSizes (minBufferSize, maxBufferSize, preferredBufferSize, bufferGranularity);
double currentRate = getSampleRate();
@@ -1226,8 +1229,8 @@ private:
updateSampleRates();
readLatencies(); // ..doing these steps because cubase does so at this stage
createDummyBuffers (preferredSize); // in initialisation, and some devices fail if we don't.
readLatencies(); // ..doing these steps because cubase does so at this stage
createDummyBuffers (preferredBufferSize); // in initialisation, and some devices fail if we don't.
readLatencies();
// start and stop because cubase does it..
@@ -1419,6 +1422,12 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ASIOAudioIODevice)
};
template <>
struct ASIOAudioIODevice::ASIOCallbackFunctions <sizeof(currentASIODev) / sizeof(currentASIODev[0])>
{
static void setCallbacksForDevice (ASIOCallbacks&, ASIOAudioIODevice*) noexcept {}
};
//==============================================================================
class ASIOAudioIODeviceType : public AudioIODeviceType
{


+ 5
- 0
source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp View File

@@ -112,6 +112,11 @@ bool check (HRESULT hr)
#define JUCE_COMCLASS(name, guid) struct __declspec (uuid (guid)) name
#endif
#if JUCE_MINGW && defined (KSDATAFORMAT_SUBTYPE_PCM)
#undef KSDATAFORMAT_SUBTYPE_PCM
#undef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
#endif
#ifndef KSDATAFORMAT_SUBTYPE_PCM
#define KSDATAFORMAT_SUBTYPE_PCM uuidFromString ("00000001-0000-0010-8000-00aa00389b71")
#define KSDATAFORMAT_SUBTYPE_IEEE_FLOAT uuidFromString ("00000003-0000-0010-8000-00aa00389b71")


+ 1
- 1
source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/metadata.h View File

@@ -33,7 +33,7 @@
#ifndef FLAC__PRIVATE__METADATA_H
#define FLAC__PRIVATE__METADATA_H
#include "FLAC/metadata.h"
#include "../../../metadata.h"
/* WATCHOUT: all malloc()ed data in the block is free()ed; this may not
* be a consistent state (e.g. PICTURE) or equivalent to the initial


+ 18
- 9
source/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp View File

@@ -112,19 +112,28 @@ struct CoreAudioFormatMetatdata
};
//==============================================================================
struct UserDefinedChunk
static StringPairArray parseUserDefinedChunk (InputStream& input, int64 size)
{
UserDefinedChunk (InputStream& input, int64 size)
StringPairArray infoStrings;
const int64 originalPosition = input.getPosition();
uint8 uuid[16];
input.read (uuid, sizeof (uuid));
if (memcmp (uuid, "\x29\x81\x92\x73\xB5\xBF\x4A\xEF\xB7\x8D\x62\xD1\xEF\x90\xBB\x2C", 16) == 0)
{
// a user defined chunk contains 16 bytes of a UUID first
uuid[1] = input.readInt64BigEndian();
uuid[0] = input.readInt64BigEndian();
const uint32 numEntries = (uint32) input.readIntBigEndian();
input.skipNextBytes (size - 16);
for (uint32 i = 0; i < numEntries && input.getPosition() < originalPosition + size; ++i)
{
String keyName = input.readString();
infoStrings.set (keyName, input.readString());
}
}
int64 uuid[2];
};
input.setPosition (originalPosition + size);
return infoStrings;
}
//==============================================================================
static StringPairArray parseMidiChunk (InputStream& input, int64 size)
@@ -288,7 +297,7 @@ struct CoreAudioFormatMetatdata
}
else if (chunkHeader.chunkType == chunkName ("uuid"))
{
UserDefinedChunk userDefinedChunk (input, chunkHeader.chunkSize);
metadataValues.addArray (parseUserDefinedChunk (input, chunkHeader.chunkSize));
}
else if (chunkHeader.chunkType == chunkName ("data"))
{


+ 0
- 4
source/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.cpp View File

@@ -27,15 +27,11 @@
} // (juce namespace)
#if ! JUCE_WINDOWS
#define Point CarbonDummyPointName // (workaround to avoid definition of "Point" by old Carbon headers)
#define Component CarbonDummyCompName
#include <QuickTime/Movies.h>
#include <QuickTime/QTML.h>
#include <QuickTime/QuickTimeComponents.h>
#include <QuickTime/MediaHandlers.h>
#include <QuickTime/ImageCodec.h>
#undef Point
#undef Component
#else
#if JUCE_MSVC
#pragma warning (push)


+ 4
- 10
source/modules/juce_audio_formats/juce_audio_formats.cpp View File

@@ -31,19 +31,18 @@
#error "Incorrect use of JUCE cpp file"
#endif
#include "../juce_core/native/juce_BasicNativeHeaders.h"
#define JUCE_CORE_INCLUDE_COM_SMART_PTR 1
#define JUCE_CORE_INCLUDE_JNI_HELPERS 1
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1
#include "juce_audio_formats.h"
//==============================================================================
#if JUCE_MAC
#define Point CarbonDummyPointName
#define Component CarbonDummyCompName
#if JUCE_QUICKTIME
#import <QTKit/QTKit.h>
#endif
#include <AudioToolbox/AudioToolbox.h>
#undef Component
#undef Point
#elif JUCE_IOS
#import <AudioToolbox/AudioToolbox.h>
@@ -85,14 +84,9 @@ namespace juce
{
#if JUCE_ANDROID
#include "../juce_core/native/juce_android_JNIHelpers.h"
#undef JUCE_QUICKTIME
#endif
#if JUCE_WINDOWS
#include "../juce_core/native/juce_win32_ComSmartPtr.h"
#endif
#include "format/juce_AudioFormat.cpp"
#include "format/juce_AudioFormatManager.cpp"
#include "format/juce_AudioFormatReader.cpp"


+ 2
- 2
source/modules/juce_audio_formats/juce_audio_formats.h View File

@@ -27,7 +27,7 @@
#include "../juce_audio_basics/juce_audio_basics.h"
//=============================================================================
//==============================================================================
/** Config: JUCE_USE_FLAC
Enables the FLAC audio codec classes (available on all platforms).
If your app doesn't need to read FLAC files, you might want to disable this to
@@ -81,7 +81,7 @@
#define JUCE_USE_WINDOWS_MEDIA_FORMAT 0
#endif
//=============================================================================
//==============================================================================
namespace juce
{


+ 0
- 5
source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm View File

@@ -387,11 +387,6 @@ public:
void* getPlatformSpecificData() override { return audioUnit; }
const String getName() const override { return pluginName; }
bool silenceInProducesSilenceOut() const override
{
return getTailLengthSeconds() <= 0;
}
double getTailLengthSeconds() const override
{
Float64 tail = 0;


+ 0
- 1
source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp View File

@@ -252,7 +252,6 @@ public:
bool acceptsMidi() const { return false; }
bool producesMidi() const { return false; }
bool silenceInProducesSilenceOut() const { return plugin == nullptr; } // ..any way to get a proper answer for these?
double getTailLengthSeconds() const { return 0.0; }
//==============================================================================


+ 39
- 39
source/modules/juce_audio_processors/format_types/juce_VST3Common.h View File

@@ -143,25 +143,25 @@ static inline Steinberg::Vst::Speaker getSpeakerType (AudioChannelSet::ChannelTy
switch (type)
{
case AudioChannelSet::ChannelType::left: return kSpeakerL;
case AudioChannelSet::ChannelType::right: return kSpeakerR;
case AudioChannelSet::ChannelType::centre: return kSpeakerC;
case AudioChannelSet::ChannelType::subbass: return kSpeakerLfe;
case AudioChannelSet::ChannelType::surroundLeft: return kSpeakerLs;
case AudioChannelSet::ChannelType::surroundRight: return kSpeakerRs;
case AudioChannelSet::ChannelType::centreLeft: return kSpeakerLc;
case AudioChannelSet::ChannelType::centreRight: return kSpeakerRc;
case AudioChannelSet::ChannelType::surround: return kSpeakerS;
case AudioChannelSet::ChannelType::sideLeft: return kSpeakerSl;
case AudioChannelSet::ChannelType::sideRight: return kSpeakerSr;
case AudioChannelSet::ChannelType::topMiddle: return kSpeakerTm;
case AudioChannelSet::ChannelType::topFrontLeft: return kSpeakerTfl;
case AudioChannelSet::ChannelType::topFrontCentre: return kSpeakerTfc;
case AudioChannelSet::ChannelType::topFrontRight: return kSpeakerTfr;
case AudioChannelSet::ChannelType::topRearLeft: return kSpeakerTrl;
case AudioChannelSet::ChannelType::topRearCentre: return kSpeakerTrc;
case AudioChannelSet::ChannelType::topRearRight: return kSpeakerTrr;
case AudioChannelSet::ChannelType::subbass2: return kSpeakerLfe2;
case AudioChannelSet::left: return kSpeakerL;
case AudioChannelSet::right: return kSpeakerR;
case AudioChannelSet::centre: return kSpeakerC;
case AudioChannelSet::subbass: return kSpeakerLfe;
case AudioChannelSet::surroundLeft: return kSpeakerLs;
case AudioChannelSet::surroundRight: return kSpeakerRs;
case AudioChannelSet::centreLeft: return kSpeakerLc;
case AudioChannelSet::centreRight: return kSpeakerRc;
case AudioChannelSet::surround: return kSpeakerS;
case AudioChannelSet::sideLeft: return kSpeakerSl;
case AudioChannelSet::sideRight: return kSpeakerSr;
case AudioChannelSet::topMiddle: return kSpeakerTm;
case AudioChannelSet::topFrontLeft: return kSpeakerTfl;
case AudioChannelSet::topFrontCentre: return kSpeakerTfc;
case AudioChannelSet::topFrontRight: return kSpeakerTfr;
case AudioChannelSet::topRearLeft: return kSpeakerTrl;
case AudioChannelSet::topRearCentre: return kSpeakerTrc;
case AudioChannelSet::topRearRight: return kSpeakerTrr;
case AudioChannelSet::subbass2: return kSpeakerLfe2;
default: break;
}
@@ -174,29 +174,29 @@ static inline AudioChannelSet::ChannelType getChannelType (Steinberg::Vst::Speak
switch (type)
{
case kSpeakerL: return AudioChannelSet::ChannelType::left;
case kSpeakerR: return AudioChannelSet::ChannelType::right;
case kSpeakerC: return AudioChannelSet::ChannelType::centre;
case kSpeakerLfe: return AudioChannelSet::ChannelType::subbass;
case kSpeakerLs: return AudioChannelSet::ChannelType::surroundLeft;
case kSpeakerRs: return AudioChannelSet::ChannelType::surroundRight;
case kSpeakerLc: return AudioChannelSet::ChannelType::centreLeft;
case kSpeakerRc: return AudioChannelSet::ChannelType::centreRight;
case kSpeakerS: return AudioChannelSet::ChannelType::surround;
case kSpeakerSl: return AudioChannelSet::ChannelType::sideLeft;
case kSpeakerSr: return AudioChannelSet::ChannelType::sideRight;
case kSpeakerTm: return AudioChannelSet::ChannelType::topMiddle;
case kSpeakerTfl: return AudioChannelSet::ChannelType::topFrontLeft;
case kSpeakerTfc: return AudioChannelSet::ChannelType::topFrontCentre;
case kSpeakerTfr: return AudioChannelSet::ChannelType::topFrontRight;
case kSpeakerTrl: return AudioChannelSet::ChannelType::topRearLeft;
case kSpeakerTrc: return AudioChannelSet::ChannelType::topRearCentre;
case kSpeakerTrr: return AudioChannelSet::ChannelType::topRearRight;
case kSpeakerLfe2: return AudioChannelSet::ChannelType::subbass2;
case kSpeakerL: return AudioChannelSet::left;
case kSpeakerR: return AudioChannelSet::right;
case kSpeakerC: return AudioChannelSet::centre;
case kSpeakerLfe: return AudioChannelSet::subbass;
case kSpeakerLs: return AudioChannelSet::surroundLeft;
case kSpeakerRs: return AudioChannelSet::surroundRight;
case kSpeakerLc: return AudioChannelSet::centreLeft;
case kSpeakerRc: return AudioChannelSet::centreRight;
case kSpeakerS: return AudioChannelSet::surround;
case kSpeakerSl: return AudioChannelSet::sideLeft;
case kSpeakerSr: return AudioChannelSet::sideRight;
case kSpeakerTm: return AudioChannelSet::topMiddle;
case kSpeakerTfl: return AudioChannelSet::topFrontLeft;
case kSpeakerTfc: return AudioChannelSet::topFrontCentre;
case kSpeakerTfr: return AudioChannelSet::topFrontRight;
case kSpeakerTrl: return AudioChannelSet::topRearLeft;
case kSpeakerTrc: return AudioChannelSet::topRearCentre;
case kSpeakerTrr: return AudioChannelSet::topRearRight;
case kSpeakerLfe2: return AudioChannelSet::subbass2;
default: break;
}
return AudioChannelSet::ChannelType::unknown;
return AudioChannelSet::unknown;
}
static inline Steinberg::Vst::SpeakerArrangement getSpeakerArrangement (const AudioChannelSet& channels) noexcept


+ 0
- 8
source/modules/juce_audio_processors/format_types/juce_VST3Headers.h View File

@@ -25,9 +25,6 @@
#ifndef JUCE_VST3HEADERS_H_INCLUDED
#define JUCE_VST3HEADERS_H_INCLUDED
#undef Point
#undef Component
// Wow, those Steinberg guys really don't worry too much about compiler warnings.
#if _MSC_VER
#pragma warning (disable: 4505)
@@ -85,8 +82,6 @@
#if JUCE_MINGW
#define _set_abort_behavior(...)
#endif
#define Point CarbonDummyPointName // The VST headers include some system headers that need
// to match the name our hacky Carbon workaround used.
#include <base/source/baseiids.cpp>
#include <base/source/fatomic.cpp>
#include <base/source/fbuffer.cpp>
@@ -112,7 +107,6 @@
#include <public.sdk/source/vst/vstcomponentbase.cpp>
#include <public.sdk/source/vst/vstparameters.cpp>
#include <public.sdk/source/vst/hosting/hostclasses.cpp>
#undef Point
//==============================================================================
namespace Steinberg
@@ -177,7 +171,5 @@ namespace Steinberg
#undef DEF_CLASS2
#undef DEF_CLASS_W
#undef END_FACTORY
#undef Point
#undef Component
#endif // JUCE_VST3HEADERS_H_INCLUDED

+ 3
- 11
source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp View File

@@ -1861,23 +1861,15 @@ public:
bool producesMidi() const override { return getBusInfo (false, false).channelCount > 0; }
//==============================================================================
bool silenceInProducesSilenceOut() const override
{
if (processor != nullptr)
return processor->getTailSamples() == Vst::kNoTail;
return true;
}
/** May return a negative value as a means of informing us that the plugin has "infinite tail," or 0 for "no tail." */
double getTailLengthSeconds() const override
{
if (processor != nullptr)
{
const double currentSampleRate = getSampleRate();
const double sampleRate = getSampleRate();
if (currentSampleRate > 0.0)
return jlimit (0, 0x7fffffff, (int) processor->getTailSamples()) / currentSampleRate;
if (sampleRate > 0.0)
return jlimit (0, 0x7fffffff, (int) processor->getTailSamples()) / sampleRate;
}
return 0.0;


+ 5
- 10
source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp View File

@@ -890,23 +890,18 @@ public:
return uid;
}
bool silenceInProducesSilenceOut() const override
{
return effect == nullptr || (effect->flags & effFlagsNoSoundInStop) != 0;
}
double getTailLengthSeconds() const override
{
if (effect == nullptr)
return 0.0;
const double currentSampleRate = getSampleRate();
const double sampleRate = getSampleRate();
if (currentSampleRate <= 0)
if (sampleRate <= 0)
return 0.0;
VstIntPtr samples = dispatch (effGetTailSize, 0, 0, 0, 0);
return samples / currentSampleRate;
return samples / sampleRate;
}
bool acceptsMidi() const override { return wantsMidiMessages; }
@@ -2915,11 +2910,11 @@ FileSearchPath VSTPluginFormat::getDefaultLocationsToSearch()
const String programFiles (File::getSpecialLocation (File::globalApplicationsDirectory).getFullPathName());
FileSearchPath paths;
paths.add (WindowsRegistry::getValue ("HKLM\\Software\\VST\\VSTPluginsPath",
paths.add (WindowsRegistry::getValue ("HKEY_LOCAL_MACHINE\\Software\\VST\\VSTPluginsPath",
programFiles + "\\Steinberg\\VstPlugins"));
paths.removeNonExistentPaths();
paths.add (WindowsRegistry::getValue ("HKLM\\Software\\VST\\VSTPluginsPath",
paths.add (WindowsRegistry::getValue ("HKEY_LOCAL_MACHINE\\Software\\VST\\VSTPluginsPath",
programFiles + "\\VstPlugins"));
return paths;
#endif


+ 2
- 5
source/modules/juce_audio_processors/juce_audio_processors.cpp View File

@@ -31,7 +31,8 @@
#error "Incorrect use of JUCE cpp file"
#endif
#include "../juce_core/native/juce_BasicNativeHeaders.h"
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1
#include "juce_audio_processors.h"
#include "../juce_gui_extra/juce_gui_extra.h"
@@ -40,11 +41,7 @@
#if JUCE_SUPPORT_CARBON \
&& ((JUCE_PLUGINHOST_VST || JUCE_PLUGINHOST_AU) \
|| ! (defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6))
#define Point CarbonDummyPointName // (workaround to avoid definition of "Point" by old Carbon headers)
#define Component CarbonDummyCompName
#include <Carbon/Carbon.h>
#undef Point
#undef Component
#endif
#endif


+ 3
- 3
source/modules/juce_audio_processors/juce_audio_processors.h View File

@@ -29,7 +29,7 @@
#include "../juce_audio_basics/juce_audio_basics.h"
//=============================================================================
//==============================================================================
/** Config: JUCE_PLUGINHOST_VST
Enables the VST audio plugin hosting classes. This requires the Steinberg VST SDK to be
installed on your machine.
@@ -67,8 +67,8 @@
#define JUCE_SUPPORT_CARBON 1
#endif
//=============================================================================
//=============================================================================
//==============================================================================
//==============================================================================
namespace juce
{


+ 18
- 12
source/modules/juce_audio_processors/processors/juce_AudioChannelSet.cpp View File

@@ -28,8 +28,11 @@ bool AudioChannelSet::operator== (const AudioChannelSet& other) const noexcept
bool AudioChannelSet::operator!= (const AudioChannelSet& other) const noexcept { return channels != other.channels; }
bool AudioChannelSet::operator< (const AudioChannelSet& other) const noexcept { return channels < other.channels; }
const char* AudioChannelSet::getChannelTypeName (AudioChannelSet::ChannelType type) noexcept
String AudioChannelSet::getChannelTypeName (AudioChannelSet::ChannelType type)
{
if (type >= discreteChannel0)
return String ("Discrete ") + String (type - discreteChannel0 + 1);
switch (type)
{
case left: return NEEDS_TRANS("Left");
@@ -63,8 +66,11 @@ const char* AudioChannelSet::getChannelTypeName (AudioChannelSet::ChannelType ty
return "Unknown";
}
const char* AudioChannelSet::getAbbreviatedChannelTypeName (AudioChannelSet::ChannelType type) noexcept
String AudioChannelSet::getAbbreviatedChannelTypeName (AudioChannelSet::ChannelType type)
{
if (type >= discreteChannel0)
return String (type - discreteChannel0 + 1);
switch (type)
{
case left: return "L";
@@ -151,17 +157,17 @@ AudioChannelSet AudioChannelSet::mono() { return AudioChannelSet (
AudioChannelSet AudioChannelSet::stereo() { return AudioChannelSet ((1u << left) | (1u << right)); }
AudioChannelSet AudioChannelSet::createLCR() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre)); }
AudioChannelSet AudioChannelSet::createLCRS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surround)); }
AudioChannelSet AudioChannelSet::quadraphonic() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << sideLeft) | (1u << sideRight)); }
AudioChannelSet AudioChannelSet::pentagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << sideLeft) | (1u << sideRight) | (1u << centre)); }
AudioChannelSet AudioChannelSet::hexagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << sideLeft) | (1u << sideRight) | (1u << centre) | (1u << surround)); }
AudioChannelSet AudioChannelSet::octagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << sideLeft) | (1u << sideRight) | (1u << centre) | (1u << surround) | (1u << wideLeft) | (1u << wideRight)); }
AudioChannelSet AudioChannelSet::quadraphonic() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << surroundLeft) | (1u << surroundRight)); }
AudioChannelSet AudioChannelSet::pentagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << surroundLeft) | (1u << surroundRight) | (1u << centre)); }
AudioChannelSet AudioChannelSet::hexagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << surroundLeft) | (1u << surroundRight) | (1u << centre) | (1u << surround)); }
AudioChannelSet AudioChannelSet::octagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << surroundLeft) | (1u << surroundRight) | (1u << centre) | (1u << surround) | (1u << wideLeft) | (1u << wideRight)); }
AudioChannelSet AudioChannelSet::ambisonic() { return AudioChannelSet ((1u << ambisonicW) | (1u << ambisonicX) | (1u << ambisonicY) | (1u << ambisonicZ)); }
AudioChannelSet AudioChannelSet::create5point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surroundLeft) | (1u << surroundRight)); }
AudioChannelSet AudioChannelSet::create5point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << surroundLeft) | (1u << surroundRight)); }
AudioChannelSet AudioChannelSet::create6point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surroundLeft) | (1u << surroundRight) | (1u << surround)); }
AudioChannelSet AudioChannelSet::create6point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << surroundLeft) | (1u << surroundRight) | (1u << surround)); }
AudioChannelSet AudioChannelSet::create7point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surroundLeft) | (1u << surroundRight) | (1u << topRearLeft) | (1u << topRearRight)); }
AudioChannelSet AudioChannelSet::create7point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << surroundLeft) | (1u << surroundRight) | (1u << topRearLeft) | (1u << topRearRight)); }
AudioChannelSet AudioChannelSet::create5point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << sideLeft) | (1u << sideRight)); }
AudioChannelSet AudioChannelSet::create5point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << sideLeft) | (1u << sideRight)); }
AudioChannelSet AudioChannelSet::create6point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << sideLeft) | (1u << sideRight) | (1u << surround)); }
AudioChannelSet AudioChannelSet::create6point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << sideLeft) | (1u << sideRight) | (1u << surround)); }
AudioChannelSet AudioChannelSet::create7point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << sideLeft) | (1u << sideRight) | (1u << surroundLeft) | (1u << surroundRight)); }
AudioChannelSet AudioChannelSet::create7point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << sideLeft) | (1u << sideRight) | (1u << surroundLeft) | (1u << surroundRight)); }
AudioChannelSet AudioChannelSet::createFront7point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surroundLeft) | (1u << surroundRight) | (1u << centreLeft) | (1u << centreRight)); }
AudioChannelSet AudioChannelSet::createFront7point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << surroundLeft) | (1u << surroundRight) | (1u << centreLeft) | (1u << centreRight)); }


+ 2
- 2
source/modules/juce_audio_processors/processors/juce_AudioChannelSet.h View File

@@ -145,10 +145,10 @@ public:
};
/** Returns the name of a given channel type. For example, this method may return "Surround Left". */
static const char* getChannelTypeName (ChannelType) noexcept;
static String getChannelTypeName (ChannelType);
/** Returns the abbreviated name of a channel type. For example, this method may return "Ls". */
static const char* getAbbreviatedChannelTypeName (ChannelType) noexcept;
static String getAbbreviatedChannelTypeName (ChannelType);
//==============================================================================
/** Adds a channel to the set. */


+ 14
- 12
source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp View File

@@ -32,7 +32,7 @@ void JUCE_CALLTYPE AudioProcessor::setTypeOfNextNewPlugin (AudioProcessor::Wrapp
AudioProcessor::AudioProcessor()
: wrapperType (wrapperTypeBeingCreated.get()),
playHead (nullptr),
sampleRate (0),
currentSampleRate (0),
blockSize (0),
latencySamples (0),
#if JUCE_DEBUG
@@ -42,23 +42,25 @@ AudioProcessor::AudioProcessor()
nonRealtime (false),
processingPrecision (singlePrecision)
{
#if ! JucePlugin_IsMidiEffect
#ifdef JucePlugin_PreferredChannelConfigurations
const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations };
#else
const short channelConfigs[][2] = { {2, 2} };
#endif
int numChannelConfigs = sizeof (channelConfigs) / sizeof (*channelConfigs);
if (numChannelConfigs > 0)
{
#if ! JucePlugin_IsSynth
busArrangement.inputBuses.add (AudioProcessorBus ("Input", AudioChannelSet::canonicalChannelSet (channelConfigs[0][0])));
#endif
busArrangement.outputBuses.add (AudioProcessorBus ("Output", AudioChannelSet::canonicalChannelSet (channelConfigs[0][1])));
}
#endif
#if ! JucePlugin_IsMidiEffect
#if ! JucePlugin_IsSynth
busArrangement.inputBuses.add (AudioProcessorBus ("Input", AudioChannelSet::canonicalChannelSet (channelConfigs[0][0])));
#endif
busArrangement.outputBuses.add (AudioProcessorBus ("Output", AudioChannelSet::canonicalChannelSet (channelConfigs[0][1])));
#ifdef JucePlugin_PreferredChannelConfigurations
#if ! JucePlugin_IsSynth
AudioProcessor::setPreferredBusArrangement (true, 0, AudioChannelSet::stereo());
#endif
AudioProcessor::setPreferredBusArrangement (false, 0, AudioChannelSet::stereo());
#endif
#endif
updateSpeakerFormatStrings();
}
@@ -123,7 +125,7 @@ void AudioProcessor::setPlayConfigDetails (const int newNumIns,
void AudioProcessor::setRateAndBufferSizeDetails (double newSampleRate, int newBlockSize) noexcept
{
sampleRate = newSampleRate;
currentSampleRate = newSampleRate;
blockSize = newBlockSize;
}


+ 16
- 8
source/modules/juce_audio_processors/processors/juce_AudioProcessor.h View File

@@ -317,6 +317,14 @@ public:
changing the channel layout of other buses, for example, if your plug-in requires the same
number of input and output channels.
For most basic plug-ins, which do not require side-chains, aux buses or detailed audio
channel layout information, it is easier to specify the acceptable channel configurations
via the "PlugIn Channel Configurations" field in the Introjucer. In this case, you should
not override this method.
If, on the other hand, you decide to override this method then you need to make sure that
"PlugIn Channel Configurations" field in the Introjucer is empty.
Note, that you must not do any heavy allocations or calculations in this callback as it may
be called several hundred times during initialization. If you require any layout specific
allocations then defer these to prepareToPlay callback.
@@ -423,7 +431,7 @@ public:
This can be called from your processBlock() method - it's not guaranteed
to be valid at any other time, and may return 0 if it's unknown.
*/
double getSampleRate() const noexcept { return sampleRate; }
double getSampleRate() const noexcept { return currentSampleRate; }
/** Returns the current typical block size that is being used.
@@ -453,9 +461,6 @@ public:
*/
void setLatencySamples (int newLatency);
/** Returns true if a silent input always produces a silent output. */
virtual bool silenceInProducesSilenceOut() const = 0;
/** Returns the length of the filter's tail, in seconds. */
virtual double getTailLengthSeconds() const = 0;
@@ -883,7 +888,7 @@ public:
//==============================================================================
/** This is called by the processor to specify its details before being played. Use this
version of the function if you are not interested in any sidechain or aux buses
version of the function if you are not interested in any sidechain and/or aux buses
and do not care about the layout of channels. Otherwise use setRateAndBufferSizeDetails.*/
void setPlayConfigDetails (int numIns, int numOuts, double sampleRate, int blockSize);
@@ -919,7 +924,7 @@ public:
WrapperType wrapperType;
//==============================================================================
#ifndef DOXYGEN
#ifndef DOXYGEN
/** Deprecated: use getTotalNumInputChannels instead. */
JUCE_DEPRECATED_WITH_BODY (int getNumInputChannels() const noexcept, { return getTotalNumInputChannels(); })
JUCE_DEPRECATED_WITH_BODY (int getNumOutputChannels() const noexcept, { return getTotalNumOutputChannels(); })
@@ -946,7 +951,7 @@ public:
the constructor. */
JUCE_DEPRECATED (virtual bool isInputChannelStereoPair (int index) const);
JUCE_DEPRECATED (virtual bool isOutputChannelStereoPair (int index) const);
#endif
#endif
//==============================================================================
/** Helper function that just converts an xml element into a binary blob.
@@ -982,7 +987,7 @@ private:
#if ! JUCE_AUDIO_PROCESSOR_NO_GUI
Component::SafePointer<AudioProcessorEditor> activeEditor;
#endif
double sampleRate;
double currentSampleRate;
int blockSize, latencySamples;
#if JUCE_DEBUG
bool textRecursionCheck;
@@ -1005,6 +1010,9 @@ private:
void disableNonMainBuses (bool isInput);
void updateSpeakerFormatStrings();
// This method is no longer used - you can delete it from your AudioProcessor classes.
JUCE_DEPRECATED_WITH_BODY (virtual bool silenceInProducesSilenceOut() const, { return false; });
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessor)
};


+ 0
- 6
source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp View File

@@ -1460,7 +1460,6 @@ void AudioProcessorGraph::processAudio (AudioBuffer<FloatType>& buffer, MidiBuff
midiMessages.addEvents (currentMidiOutputBuffer, 0, buffer.getNumSamples(), 0);
}
bool AudioProcessorGraph::silenceInProducesSilenceOut() const { return false; }
double AudioProcessorGraph::getTailLengthSeconds() const { return 0; }
bool AudioProcessorGraph::acceptsMidi() const { return true; }
bool AudioProcessorGraph::producesMidi() const { return true; }
@@ -1601,11 +1600,6 @@ void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioBuffer<doubl
processAudio (buffer, midiMessages);
}
bool AudioProcessorGraph::AudioGraphIOProcessor::silenceInProducesSilenceOut() const
{
return isOutput();
}
double AudioProcessorGraph::AudioGraphIOProcessor::getTailLengthSeconds() const
{
return 0;


+ 0
- 2
source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h View File

@@ -311,7 +311,6 @@ public:
void processBlock (AudioBuffer<double>&, MidiBuffer&) override;
bool supportsDoublePrecisionProcessing() const override;
bool silenceInProducesSilenceOut() const override;
double getTailLengthSeconds() const override;
bool acceptsMidi() const override;
bool producesMidi() const override;
@@ -356,7 +355,6 @@ public:
void setNonRealtime (bool) noexcept override;
void setPlayHead (AudioPlayHead*) override;
bool silenceInProducesSilenceOut() const override;
double getTailLengthSeconds() const override;
bool acceptsMidi() const override;
bool producesMidi() const override;


+ 14
- 17
source/modules/juce_audio_processors/utilities/juce_AudioProcessorParameters.cpp View File

@@ -50,14 +50,17 @@ void AudioParameterFloat::setValue (float newValue) { value
float AudioParameterFloat::getDefaultValue() const { return range.convertTo0to1 (defaultValue); }
int AudioParameterFloat::getNumSteps() const { return AudioProcessorParameterWithID::getNumSteps(); }
float AudioParameterFloat::getValueForText (const String& text) const { return range.convertTo0to1 (text.getFloatValue()); }
String AudioParameterFloat::getText (float v, int length) const { return String (range.convertFrom0to1 (v), 2).substring (0, length); }
AudioParameterFloat& AudioParameterFloat::operator= (float newValue)
String AudioParameterFloat::getText (float v, int length) const
{
const float normalisedValue = range.convertTo0to1 (newValue);
String asText (range.convertFrom0to1 (v), 2);
return length > 0 ? asText.substring (0, length) : asText;
}
if (value != normalisedValue)
setValueNotifyingHost (normalisedValue);
AudioParameterFloat& AudioParameterFloat::operator= (float newValue)
{
if (value != newValue)
setValueNotifyingHost (range.convertTo0to1 (newValue));
return *this;
}
@@ -87,10 +90,8 @@ String AudioParameterInt::getText (float v, int /*length*/) const { retur
AudioParameterInt& AudioParameterInt::operator= (int newValue)
{
const float normalisedValue = convertTo0to1 (newValue);
if (value != normalisedValue)
setValueNotifyingHost (normalisedValue);
if (get() != newValue)
setValueNotifyingHost (convertTo0to1 (newValue));
return *this;
}
@@ -115,10 +116,8 @@ String AudioParameterBool::getText (float v, int /*length*/) const { retur
AudioParameterBool& AudioParameterBool::operator= (bool newValue)
{
const float normalisedValue = newValue ? 1.0f : 0.0f;
if (value != normalisedValue)
setValueNotifyingHost (normalisedValue);
if (get() != newValue)
setValueNotifyingHost (newValue ? 1.0f : 0.0f);
return *this;
}
@@ -148,10 +147,8 @@ String AudioParameterChoice::getText (float v, int /*length*/) const { retur
AudioParameterChoice& AudioParameterChoice::operator= (int newValue)
{
const float normalisedValue = convertTo0to1 (newValue);
if (value != normalisedValue)
setValueNotifyingHost (normalisedValue);
if (getIndex() != newValue)
setValueNotifyingHost (convertTo0to1 (newValue));
return *this;
}

+ 6
- 6
source/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.cpp View File

@@ -258,21 +258,21 @@ void AudioProcessorValueTreeState::updateParameterConnectionsToChildTrees()
}
}
void AudioProcessorValueTreeState::valueTreePropertyChanged (ValueTree&, const Identifier& property)
void AudioProcessorValueTreeState::valueTreePropertyChanged (ValueTree& tree, const Identifier& property)
{
if (property == idPropertyID)
if (property == idPropertyID && tree.hasType (valueType) && tree.getParent() == state)
updateParameterConnectionsToChildTrees();
}
void AudioProcessorValueTreeState::valueTreeChildAdded (ValueTree& parent, ValueTree&)
void AudioProcessorValueTreeState::valueTreeChildAdded (ValueTree& parent, ValueTree& tree)
{
if (parent == state)
if (parent == state && tree.hasType (valueType))
updateParameterConnectionsToChildTrees();
}
void AudioProcessorValueTreeState::valueTreeChildRemoved (ValueTree& parent, ValueTree&, int)
void AudioProcessorValueTreeState::valueTreeChildRemoved (ValueTree& parent, ValueTree& tree, int)
{
if (parent == state)
if (parent == state && tree.hasType (valueType))
updateParameterConnectionsToChildTrees();
}


+ 7
- 0
source/modules/juce_core/containers/juce_HashMap.h View File

@@ -424,6 +424,13 @@ public:
return entry != nullptr ? entry->value : ValueType();
}
/** Resets the iterator to its starting position. */
void reset() noexcept
{
entry = nullptr;
index = 0;
}
private:
//==============================================================================
const HashMap& hashMap;


+ 62
- 22
source/modules/juce_core/files/juce_File.cpp View File

@@ -70,8 +70,24 @@ File& File::operator= (File&& other) noexcept
const File File::nonexistent;
//==============================================================================
static String removeEllipsis (const String& path)
{
StringArray toks;
toks.addTokens (path, File::separatorString, StringRef());
for (int i = 1; i < toks.size(); ++i)
{
if (toks[i] == ".." && toks[i - 1] != "..")
{
toks.removeRange (i - 1, 2);
i = jmax (0, i - 2);
}
}
return toks.joinIntoString (File::separatorString);
}
String File::parseAbsolutePath (const String& p)
{
if (p.isEmpty())
@@ -81,6 +97,9 @@ String File::parseAbsolutePath (const String& p)
// Windows..
String path (p.replaceCharacter ('/', '\\'));
if (path.contains ("\\..\\"))
path = removeEllipsis (path);
if (path.startsWithChar (separator))
{
if (path[1] != separator)
@@ -120,6 +139,9 @@ String File::parseAbsolutePath (const String& p)
String path (p);
if (path.contains ("/../"))
path = removeEllipsis (path);
if (path.startsWithChar ('~'))
{
if (path[1] == separator || path[1] == 0)
@@ -347,62 +369,69 @@ int64 File::hashCode64() const { return fullPath.hashCode64(); }
//==============================================================================
bool File::isAbsolutePath (StringRef path)
{
return path.text[0] == separator
const juce_wchar firstChar = *(path.text);
return firstChar == separator
#if JUCE_WINDOWS
|| (path.isNotEmpty() && path.text[1] == ':');
|| (firstChar != 0 && path.text[1] == ':');
#else
|| path.text[0] == '~';
|| firstChar == '~';
#endif
}
File File::getChildFile (StringRef relativePath) const
{
if (isAbsolutePath (relativePath))
return File (String (relativePath.text));
String::CharPointerType r = relativePath.text;
if (relativePath[0] != '.')
return File (addTrailingSeparator (fullPath) + relativePath);
if (isAbsolutePath (r))
return File (String (r));
String path (fullPath);
// It's relative, so remove any ../ or ./ bits at the start..
#if JUCE_WINDOWS
if (relativePath.text.indexOf ((juce_wchar) '/') >= 0)
return getChildFile (String (relativePath.text).replaceCharacter ('/', '\\'));
if (r.indexOf ((juce_wchar) '/') >= 0)
return getChildFile (String (r).replaceCharacter ('/', '\\'));
#endif
while (relativePath[0] == '.')
String path (fullPath);
while (*r == '.')
{
const juce_wchar secondChar = relativePath[1];
String::CharPointerType lastPos = r;
const juce_wchar secondChar = *++r;
if (secondChar == '.')
if (secondChar == '.') // remove "../"
{
const juce_wchar thirdChar = relativePath[2];
const juce_wchar thirdChar = *++r;
if (thirdChar == 0 || thirdChar == separator)
if (thirdChar == separator || thirdChar == 0)
{
const int lastSlash = path.lastIndexOfChar (separator);
if (lastSlash >= 0)
path = path.substring (0, lastSlash);
relativePath = relativePath.text + (thirdChar == 0 ? 2 : 3);
while (*r == separator) // ignore duplicate slashes
++r;
}
else
{
r = lastPos;
break;
}
}
else if (secondChar == separator)
else if (secondChar == separator || secondChar == 0) // remove "./"
{
relativePath = relativePath.text + 2;
while (*r == separator) // ignore duplicate slashes
++r;
}
else
{
r = lastPos;
break;
}
}
return File (addTrailingSeparator (path) + relativePath);
path = addTrailingSeparator (path);
path.appendCharPointer (r);
return File (path);
}
File File::getSiblingFile (StringRef fileName) const
@@ -1020,6 +1049,17 @@ public:
expect (tempFile.getSiblingFile ("foo").isAChildOf (temp));
expect (tempFile.hasWriteAccess());
expect (home.getChildFile (".") == home);
expect (home.getChildFile ("..") == home.getParentDirectory());
expect (home.getChildFile (".xyz").getFileName() == ".xyz");
expect (home.getChildFile ("..xyz").getFileName() == "..xyz");
expect (home.getChildFile ("...xyz").getFileName() == "...xyz");
expect (home.getChildFile ("./xyz") == home.getChildFile ("xyz"));
expect (home.getChildFile ("././xyz") == home.getChildFile ("xyz"));
expect (home.getChildFile ("../xyz") == home.getParentDirectory().getChildFile ("xyz"));
expect (home.getChildFile (".././xyz") == home.getParentDirectory().getChildFile ("xyz"));
expect (home.getChildFile ("./../xyz") == home.getParentDirectory().getChildFile ("xyz"));
{
FileOutputStream fo (tempFile);
fo.write ("0123456789", 10);


+ 1
- 1
source/modules/juce_core/files/juce_File.h View File

@@ -48,7 +48,7 @@ public:
//==============================================================================
/** Creates an (invalid) file object.
The file is initially set to an empty path, so getFullPath() will return
The file is initially set to an empty path, so getFullPathName() will return
an empty string, and comparing the file to File::nonexistent will return
true.


+ 2
- 2
source/modules/juce_core/files/juce_WildcardFileFilter.h View File

@@ -66,10 +66,10 @@ public:
//==============================================================================
/** Returns true if the filename matches one of the patterns specified. */
bool isFileSuitable (const File& file) const;
bool isFileSuitable (const File& file) const override;
/** This always returns true. */
bool isDirectorySuitable (const File& file) const;
bool isDirectorySuitable (const File& file) const override;
private:
//==============================================================================


+ 57
- 8
source/modules/juce_core/javascript/juce_Javascript.cpp View File

@@ -44,7 +44,8 @@
#define JUCE_JS_KEYWORDS(X) \
X(var, "var") X(if_, "if") X(else_, "else") X(do_, "do") X(null_, "null") \
X(while_, "while") X(for_, "for") X(break_, "break") X(continue_, "continue") X(undefined, "undefined") \
X(function, "function") X(return_, "return") X(true_, "true") X(false_, "false") X(new_, "new")
X(function, "function") X(return_, "return") X(true_, "true") X(false_, "false") X(new_, "new") \
X(typeof_, "typeof")
namespace TokenTypes
{
@@ -71,6 +72,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
setMethod ("trace", trace);
setMethod ("charToInt", charToInt);
setMethod ("parseInt", IntegerClass::parseInt);
setMethod ("typeof", typeof_internal);
}
Time timeout;
@@ -97,12 +99,13 @@ struct JavascriptEngine::RootObject : public DynamicObject
&& (((a.isUndefined() || a.isVoid()) && (b.isUndefined() || b.isVoid())) || a == b);
}
static String getTokenName (TokenType t) { return t[0] == '$' ? String (t + 1) : ("'" + String (t) + "'"); }
static bool isFunction (const var& v) { return dynamic_cast<FunctionObject*> (v.getObject()) != nullptr; }
static bool isNumericOrUndefined (const var& v) { return v.isInt() || v.isDouble() || v.isInt64() || v.isBool() || v.isUndefined(); }
static int64 getOctalValue (const String& s) { BigInteger b; b.parseString (s, 8); return b.toInt64(); }
static Identifier getPrototypeIdentifier() { static const Identifier i ("prototype"); return i; }
static var* getPropertyPointer (DynamicObject* o, const Identifier& i) { return o->getProperties().getVarPointer (i); }
static String getTokenName (TokenType t) { return t[0] == '$' ? String (t + 1) : ("'" + String (t) + "'"); }
static bool isFunction (const var& v) noexcept { return dynamic_cast<FunctionObject*> (v.getObject()) != nullptr; }
static bool isNumeric (const var& v) noexcept { return v.isInt() || v.isDouble() || v.isInt64() || v.isBool(); }
static bool isNumericOrUndefined (const var& v) noexcept { return isNumeric (v) || v.isUndefined(); }
static int64 getOctalValue (const String& s) { BigInteger b; b.parseString (s, 8); return b.toInt64(); }
static Identifier getPrototypeIdentifier() { static const Identifier i ("prototype"); return i; }
static var* getPropertyPointer (DynamicObject* o, const Identifier& i) noexcept { return o->getProperties().getVarPointer (i); }
//==============================================================================
struct CodeLocation
@@ -1079,7 +1082,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
if (matchIf (TokenTypes::while_)) return parseDoOrWhileLoop (false);
if (matchIf (TokenTypes::do_)) return parseDoOrWhileLoop (true);
if (matchIf (TokenTypes::for_)) return parseForLoop();
if (matchIf (TokenTypes::return_)) return new ReturnStatement (location, matchIf (TokenTypes::semicolon) ? new Expression (location) : parseExpression());
if (matchIf (TokenTypes::return_)) return parseReturn();
if (matchIf (TokenTypes::break_)) return new BreakStatement (location);
if (matchIf (TokenTypes::continue_)) return new ContinueStatement (location);
if (matchIf (TokenTypes::function)) return parseFunction();
@@ -1111,6 +1114,16 @@ struct JavascriptEngine::RootObject : public DynamicObject
return s.release();
}
Statement* parseReturn()
{
if (matchIf (TokenTypes::semicolon))
return new ReturnStatement (location, new Expression (location));
ReturnStatement* r = new ReturnStatement (location, parseExpression());
matchIf (TokenTypes::semicolon);
return r;
}
Statement* parseVar()
{
ScopedPointer<VarStatement> s (new VarStatement (location));
@@ -1345,12 +1358,21 @@ struct JavascriptEngine::RootObject : public DynamicObject
return new PostAssignment (location, e, new OpType (location, lhs2, one));
}
Expression* parseTypeof()
{
ScopedPointer<FunctionCall> f (new FunctionCall (location));
f->object = new UnqualifiedName (location, "typeof");
f->arguments.add (parseExpression());
return f.release();
}
Expression* parseUnary()
{
if (matchIf (TokenTypes::minus)) { ExpPtr a (new LiteralValue (location, (int) 0)), b (parseUnary()); return new SubtractionOp (location, a, b); }
if (matchIf (TokenTypes::logicalNot)) { ExpPtr a (new LiteralValue (location, (int) 0)), b (parseUnary()); return new EqualsOp (location, a, b); }
if (matchIf (TokenTypes::plusplus)) return parsePreIncDec<AdditionOp>();
if (matchIf (TokenTypes::minusminus)) return parsePreIncDec<SubtractionOp>();
if (matchIf (TokenTypes::typeof_)) return parseTypeof();
return parseFactor();
}
@@ -1478,6 +1500,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
setMethod ("contains", contains);
setMethod ("remove", remove);
setMethod ("join", join);
setMethod ("push", push);
}
static Identifier getClassName() { static const Identifier i ("Array"); return i; }
@@ -1508,6 +1531,19 @@ struct JavascriptEngine::RootObject : public DynamicObject
return strings.joinIntoString (getString (a, 0));
}
static var push (Args a)
{
if (Array<var>* array = a.thisObject.getArray())
{
for (int i = 0; i < a.numArguments; ++i)
array->add (a.arguments[i]);
return array->size();
}
return var::undefined();
}
};
//==============================================================================
@@ -1638,6 +1674,19 @@ struct JavascriptEngine::RootObject : public DynamicObject
static var trace (Args a) { Logger::outputDebugString (JSON::toString (a.thisObject)); return var::undefined(); }
static var charToInt (Args a) { return (int) (getString (a, 0)[0]); }
static var typeof_internal (Args a)
{
var v (get (a, 0));
if (v.isVoid()) return "void";
if (v.isString()) return "string";
if (isNumeric (v)) return "number";
if (isFunction (v) || v.isMethod()) return "function";
if (v.isObject()) return "object";
return "undefined";
}
static var exec (Args a)
{
if (RootObject* root = dynamic_cast<RootObject*> (a.thisObject.getObject()))


+ 7
- 6
source/modules/juce_core/juce_core.cpp View File

@@ -35,7 +35,10 @@
#error "Incorrect use of JUCE cpp file"
#endif
#include "native/juce_BasicNativeHeaders.h"
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1
#define JUCE_CORE_INCLUDE_COM_SMART_PTR 1
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1
#include "juce_core.h"
#include <locale>
@@ -80,6 +83,7 @@
#if JUCE_LINUX
#include <langinfo.h>
#include <ifaddrs.h>
#include <sys/resource.h>
#if JUCE_USE_CURL
#include <curl/curl.h>
@@ -140,6 +144,7 @@ namespace juce
#include "maths/juce_Expression.cpp"
#include "maths/juce_Random.cpp"
#include "memory/juce_MemoryBlock.cpp"
#include "misc/juce_RuntimePermissions.cpp"
#include "misc/juce_Result.cpp"
#include "misc/juce_Uuid.cpp"
#include "network/juce_MACAddress.cpp"
@@ -180,10 +185,6 @@ namespace juce
#include "files/juce_WildcardFileFilter.cpp"
//==============================================================================
#if JUCE_MAC || JUCE_IOS
#include "native/juce_osx_ObjCHelpers.h"
#endif
#if JUCE_ANDROID
#include "native/juce_android_JNIHelpers.h"
#endif
@@ -203,7 +204,6 @@ namespace juce
//==============================================================================
#elif JUCE_WINDOWS
#include "native/juce_win32_ComSmartPtr.h"
#include "native/juce_win32_Files.cpp"
#include "native/juce_win32_Network.cpp"
#include "native/juce_win32_Registry.cpp"
@@ -229,6 +229,7 @@ namespace juce
#include "native/juce_android_Network.cpp"
#include "native/juce_android_SystemStats.cpp"
#include "native/juce_android_Threads.cpp"
#include "native/juce_android_RuntimePermissions.cpp"
#endif


+ 25
- 6
source/modules/juce_core/juce_core.h View File

@@ -41,7 +41,7 @@
#include "system/juce_TargetPlatform.h"
//=============================================================================
//==============================================================================
/** Config: JUCE_FORCE_DEBUG
Normally, JUCE_DEBUG is set to 1 or 0 based on compiler and project settings,
@@ -51,7 +51,7 @@
//#define JUCE_FORCE_DEBUG 0
#endif
//=============================================================================
//==============================================================================
/** Config: JUCE_LOG_ASSERTIONS
If this flag is enabled, the jassert and jassertfalse macros will always use Logger::writeToLog()
@@ -71,7 +71,7 @@
#endif
#endif
//=============================================================================
//==============================================================================
/** Config: JUCE_CHECK_MEMORY_LEAKS
Enables a memory-leak check for certain objects when the app terminates. See the LeakedObjectDetector
@@ -81,7 +81,7 @@
#define JUCE_CHECK_MEMORY_LEAKS 1
#endif
//=============================================================================
//==============================================================================
/** Config: JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES
In a Visual C++ build, this can be used to stop the required system libs being
@@ -130,8 +130,12 @@
#define JUCE_STRING_UTF_TYPE 8
#endif
//=============================================================================
//=============================================================================
//==============================================================================
//==============================================================================
#if JUCE_CORE_INCLUDE_NATIVE_HEADERS
#include "native/juce_BasicNativeHeaders.h"
#endif
#include "system/juce_StandardHeader.h"
@@ -188,6 +192,7 @@ extern JUCE_API void JUCE_CALLTYPE logAssertion (const char* file, int line) noe
#include "threads/juce_CriticalSection.h"
#include "maths/juce_Range.h"
#include "maths/juce_NormalisableRange.h"
#include "maths/juce_StatisticsAccumulator.h"
#include "containers/juce_ElementComparator.h"
#include "containers/juce_ArrayAllocationBase.h"
#include "containers/juce_Array.h"
@@ -237,6 +242,7 @@ extern JUCE_API void JUCE_CALLTYPE logAssertion (const char* file, int line) noe
#include "maths/juce_BigInteger.h"
#include "maths/juce_Expression.h"
#include "maths/juce_Random.h"
#include "misc/juce_RuntimePermissions.h"
#include "misc/juce_Uuid.h"
#include "misc/juce_WindowsRegistry.h"
#include "system/juce_SystemStats.h"
@@ -269,6 +275,19 @@ extern JUCE_API void JUCE_CALLTYPE logAssertion (const char* file, int line) noe
#include "containers/juce_PropertySet.h"
#include "memory/juce_SharedResourcePointer.h"
#if JUCE_CORE_INCLUDE_OBJC_HELPERS && (JUCE_MAC || JUCE_IOS)
#include "native/juce_osx_ObjCHelpers.h"
#endif
#if JUCE_CORE_INCLUDE_COM_SMART_PTR && JUCE_WINDOWS
#include "native/juce_win32_ComSmartPtr.h"
#endif
#if JUCE_CORE_INCLUDE_JNI_HELPERS && JUCE_ANDROID
#include "native/juce_android_JNIHelpers.h"
#endif
#ifndef DOXYGEN
/*
As the very long class names here try to explain, the purpose of this code is to cause


+ 1
- 1
source/modules/juce_core/maths/juce_BigInteger.cpp View File

@@ -315,7 +315,7 @@ inline static int highestBitInInt (uint32 n) noexcept
{
jassert (n != 0); // (the built-in functions may not work for n = 0)
#if JUCE_GCC
#if JUCE_GCC || JUCE_CLANG
return 31 - __builtin_clz (n);
#elif JUCE_USE_MSVC_INTRINSICS
unsigned long highest;


+ 10
- 6
source/modules/juce_core/maths/juce_MathsFunctions.h View File

@@ -324,9 +324,9 @@ template <>
inline float juce_hypot (float a, float b) noexcept
{
#if JUCE_MSVC
return (_hypotf (a, b));
return _hypotf (a, b);
#else
return (hypotf (a, b));
return hypotf (a, b);
#endif
}
#endif
@@ -356,12 +356,16 @@ const float float_Pi = 3.14159265358979323846f;
/** Converts an angle in degrees to radians. */
template <typename FloatType>
FloatType degreesToRadians (FloatType degrees) noexcept { return degrees * static_cast<FloatType> (double_Pi / 180.0); }
inline float degreesToRadians (float degrees) noexcept { return degrees * (float_Pi / 180.0f); }
/** Converts an angle in degrees to radians. */
inline double degreesToRadians (double degrees) noexcept { return degrees * (double_Pi / 180.0); }
/** Converts an angle in radians to degrees. */
template <typename FloatType>
FloatType radiansToDegrees (FloatType radians) noexcept { return radians * static_cast<FloatType> (180.0 / double_Pi); }
inline float radiansToDegrees (float radians) noexcept { return radians * (180.0f / float_Pi); }
/** Converts an angle in radians to degrees. */
inline double radiansToDegrees (double radians) noexcept { return radians * (180.0 / double_Pi); }
//==============================================================================


+ 145
- 0
source/modules/juce_core/maths/juce_StatisticsAccumulator.h View File

@@ -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

+ 1
- 1
source/modules/juce_core/memory/juce_Atomic.h View File

@@ -201,7 +201,7 @@ private:
#endif
//==============================================================================
#elif (JUCE_GCC || JUCE_CLANG) && ! JUCE_MSVC
#elif JUCE_GCC || JUCE_CLANG
#define JUCE_ATOMICS_GCC 1 // GCC with intrinsics
#if JUCE_IOS || JUCE_ANDROID // (64-bit ops will compile but not link on these mobile OSes)


+ 1
- 1
source/modules/juce_core/memory/juce_ByteOrder.h View File

@@ -157,7 +157,7 @@ inline uint32 ByteOrder::swap (uint32 n) noexcept
{
#if JUCE_MAC || JUCE_IOS
return OSSwapInt32 (n);
#elif JUCE_GCC && JUCE_INTEL && ! JUCE_NO_INLINE_ASM
#elif (JUCE_GCC || JUCE_CLANG) && JUCE_INTEL && ! JUCE_NO_INLINE_ASM
asm("bswap %%eax" : "=a"(n) : "a"(n));
return n;
#elif JUCE_USE_MSVC_INTRINSICS


+ 6
- 0
source/modules/juce_core/memory/juce_OptionalScopedPointer.h View File

@@ -180,6 +180,12 @@ private:
//==============================================================================
ScopedPointer<ObjectType> object;
bool shouldDelete;
// This is here to avoid people accidentally taking a second owned copy of
// a scoped pointer, which is almost certainly not what you intended to do!
// If you hit a problem with this, you probably meant to say
// myPointer.setOwned (myScopedPointer.release())
void setOwned (const ScopedPointer<ObjectType>&) JUCE_DELETED_FUNCTION;
};


+ 48
- 0
source/modules/juce_core/misc/juce_RuntimePermissions.cpp View File

@@ -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

+ 131
- 0
source/modules/juce_core/misc/juce_RuntimePermissions.h View File

@@ -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

+ 14
- 0
source/modules/juce_core/native/java/AndroidRuntimePermissions.java View File

@@ -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);
}

+ 145
- 23
source/modules/juce_core/native/java/JuceAppActivity.java View File

@@ -30,14 +30,14 @@ import android.content.DialogInterface;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Looper;
import android.os.Handler;
import android.os.Build;
import android.os.Process;
import android.os.ParcelUuid;
import android.os.Environment;
import android.view.*;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
@@ -49,19 +49,16 @@ import android.text.InputType;
import android.util.DisplayMetrics;
import android.util.Log;
import java.lang.Runnable;
import java.util.List;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.TimerTask;
import java.util.*;
import java.io.*;
import java.net.URL;
import java.net.HttpURLConnection;
import android.media.AudioManager;
import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.support.v4.content.ContextCompat;
import android.support.v4.app.ActivityCompat;
import android.Manifest;
$$JuceAndroidMidiImports$$ // If you get an error here, you need to re-save your project with the introjucer!
@@ -74,6 +71,73 @@ public class JuceAppActivity extends Activity
System.loadLibrary ("juce_jni");
}
//==============================================================================
public boolean isPermissionDeclaredInManifest (int permissionID)
{
String permissionToCheck = getAndroidPermissionName(permissionID);
try
{
PackageInfo info = getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS);
if (info.requestedPermissions != null)
for (String permission : info.requestedPermissions)
if (permission.equals (permissionToCheck))
return true;
}
catch (PackageManager.NameNotFoundException e)
{
Log.d ("JUCE", "isPermissionDeclaredInManifest: PackageManager.NameNotFoundException = " + e.toString());
}
Log.d ("JUCE", "isPermissionDeclaredInManifest: could not find requested permission " + permissionToCheck);
return false;
}
//==============================================================================
// these have to match the values of enum PermissionID in C++ class RuntimePermissions:
private static final int JUCE_PERMISSIONS_RECORD_AUDIO = 1;
private static final int JUCE_PERMISSIONS_BLUETOOTH_MIDI = 2;
private static String getAndroidPermissionName (int permissionID)
{
switch (permissionID)
{
case JUCE_PERMISSIONS_RECORD_AUDIO: return Manifest.permission.RECORD_AUDIO;
case JUCE_PERMISSIONS_BLUETOOTH_MIDI: return Manifest.permission.ACCESS_COARSE_LOCATION;
}
// unknown permission ID!
assert false;
return new String();
}
public boolean isPermissionGranted (int permissionID)
{
return ContextCompat.checkSelfPermission (this, getAndroidPermissionName (permissionID)) == PackageManager.PERMISSION_GRANTED;
}
private Map<Integer, Long> permissionCallbackPtrMap;
public void requestRuntimePermission (int permissionID, long ptrToCallback)
{
String permissionName = getAndroidPermissionName (permissionID);
if (ContextCompat.checkSelfPermission (this, permissionName) != PackageManager.PERMISSION_GRANTED)
{
// remember callbackPtr, request permissions, and let onRequestPermissionResult call callback asynchronously
permissionCallbackPtrMap.put (permissionID, ptrToCallback);
ActivityCompat.requestPermissions (this, new String[]{permissionName}, permissionID);
}
else
{
// permissions were already granted before, we can call callback directly
androidRuntimePermissionsCallback (true, ptrToCallback);
}
}
$$JuceAndroidRuntimePermissionsCode$$ // If you get an error here, you need to re-save your project with the introjucer!
//==============================================================================
public static class MidiPortID extends Object
{
@@ -132,10 +196,13 @@ public class JuceAppActivity extends Activity
super.onCreate (savedInstanceState);
isScreenSaverEnabled = true;
hideActionBar();
viewHolder = new ViewHolder (this);
setContentView (viewHolder);
setVolumeControlStream (AudioManager.STREAM_MUSIC);
permissionCallbackPtrMap = new HashMap<Integer, Long>();
}
@Override
@@ -174,6 +241,49 @@ public class JuceAppActivity extends Activity
getApplicationInfo().dataDir);
}
private void hideActionBar()
{
// get "getActionBar" method
java.lang.reflect.Method getActionBarMethod = null;
try
{
getActionBarMethod = this.getClass().getMethod ("getActionBar");
}
catch (SecurityException e) { return; }
catch (NoSuchMethodException e) { return; }
if (getActionBarMethod == null) return;
// invoke "getActionBar" method
Object actionBar = null;
try
{
actionBar = getActionBarMethod.invoke (this);
}
catch (java.lang.IllegalArgumentException e) { return; }
catch (java.lang.IllegalAccessException e) { return; }
catch (java.lang.reflect.InvocationTargetException e) { return; }
if (actionBar == null) return;
// get "hide" method
java.lang.reflect.Method actionBarHideMethod = null;
try
{
actionBarHideMethod = actionBar.getClass().getMethod ("hide");
}
catch (SecurityException e) { return; }
catch (NoSuchMethodException e) { return; }
if (actionBarHideMethod == null) return;
// invoke "hide" method
try
{
actionBarHideMethod.invoke (actionBar);
}
catch (java.lang.IllegalArgumentException e) {}
catch (java.lang.IllegalAccessException e) {}
catch (java.lang.reflect.InvocationTargetException e) {}
}
//==============================================================================
private native void launchApp (String appFile, String appDataDir);
private native void quitApp();
@@ -691,7 +801,7 @@ public class JuceAppActivity extends Activity
int format, int width, int height);
}
public NativeSurfaceView createNativeSurfaceView(long nativeSurfacePtr)
public NativeSurfaceView createNativeSurfaceView (long nativeSurfacePtr)
{
return new NativeSurfaceView (this, nativeSurfacePtr);
}
@@ -917,6 +1027,17 @@ public class JuceAppActivity extends Activity
: locale.getDisplayLanguage (java.util.Locale.US);
}
private static final String getFileLocation (String type)
{
return Environment.getExternalStoragePublicDirectory (type).getAbsolutePath();
}
public static final String getDocumentsFolder() { return Environment.getDataDirectory().getAbsolutePath(); }
public static final String getPicturesFolder() { return getFileLocation (Environment.DIRECTORY_PICTURES); }
public static final String getMusicFolder() { return getFileLocation (Environment.DIRECTORY_MUSIC); }
public static final String getMoviesFolder() { return getFileLocation (Environment.DIRECTORY_MOVIES); }
public static final String getDownloadsFolder() { return getFileLocation (Environment.DIRECTORY_DOWNLOADS); }
//==============================================================================
private final class SingleMediaScanner implements MediaScannerConnectionClient
{
@@ -1041,23 +1162,24 @@ public class JuceAppActivity extends Activity
return null;
java.lang.reflect.Method method;
try {
try
{
method = obj.getClass().getMethod ("getProperty", String.class);
} catch (SecurityException e) {
return null;
} catch (NoSuchMethodException e) {
return null;
}
catch (SecurityException e) { return null; }
catch (NoSuchMethodException e) { return null; }
if (method == null)
return null;
try {
try
{
return (String) method.invoke (obj, property);
} catch (java.lang.IllegalArgumentException e) {
} catch (java.lang.IllegalAccessException e) {
} catch (java.lang.reflect.InvocationTargetException e) {
}
catch (java.lang.IllegalArgumentException e) {}
catch (java.lang.IllegalAccessException e) {}
catch (java.lang.reflect.InvocationTargetException e) {}
return null;
}
@@ -1075,8 +1197,9 @@ public class JuceAppActivity extends Activity
private static class JuceThread extends Thread
{
public JuceThread (long host)
public JuceThread (long host, String threadName, long threadStackSize)
{
super (null, null, threadName, threadStackSize);
_this = host;
}
@@ -1089,9 +1212,8 @@ public class JuceAppActivity extends Activity
private long _this;
}
public final Thread createNewThread(long host)
public final Thread createNewThread(long host, String threadName, long threadStackSize)
{
return new JuceThread(host);
return new JuceThread(host, threadName, threadStackSize);
}
}

+ 0
- 5
source/modules/juce_core/native/juce_BasicNativeHeaders.h View File

@@ -29,7 +29,6 @@
#ifndef JUCE_BASICNATIVEHEADERS_H_INCLUDED
#define JUCE_BASICNATIVEHEADERS_H_INCLUDED
#include "../system/juce_TargetPlatform.h"
#undef T
//==============================================================================
@@ -42,12 +41,8 @@
#import <MobileCoreServices/MobileCoreServices.h>
#include <sys/fcntl.h>
#else
#define Point CarbonDummyPointName
#define Component CarbonDummyCompName
#import <Cocoa/Cocoa.h>
#import <CoreAudio/HostTime.h>
#undef Point
#undef Component
#include <sys/dir.h>
#endif


+ 11
- 7
source/modules/juce_core/native/juce_android_Files.cpp View File

@@ -46,23 +46,27 @@ String File::getVersion() const
return String();
}
static File getSpecialFile (jmethodID type)
{
return File (juceString (LocalRef<jstring> ((jstring) getEnv()->CallStaticObjectMethod (JuceAppActivity, type))));
}
File File::getSpecialLocation (const SpecialLocationType type)
{
switch (type)
{
case userHomeDirectory:
case userDocumentsDirectory:
case userMusicDirectory:
case userMoviesDirectory:
case userPicturesDirectory:
case userApplicationDataDirectory:
case userDesktopDirectory:
return File (android.appDataDir);
case commonApplicationDataDirectory:
case commonDocumentsDirectory:
return File (android.appDataDir);
case userDocumentsDirectory:
case commonDocumentsDirectory: return getSpecialFile (JuceAppActivity.getDocumentsFolder);
case userPicturesDirectory: return getSpecialFile (JuceAppActivity.getPicturesFolder);
case userMusicDirectory: return getSpecialFile (JuceAppActivity.getMusicFolder);
case userMoviesDirectory: return getSpecialFile (JuceAppActivity.getMoviesFolder);
case globalApplicationsDirectory:
return File ("/system/app");


+ 10
- 1
source/modules/juce_core/native/juce_android_JNIHelpers.h View File

@@ -266,6 +266,7 @@ extern AndroidSystem android;
METHOD (createNativeSurfaceView, "createNativeSurfaceView", "(J)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$NativeSurfaceView;") \
METHOD (postMessage, "postMessage", "(J)V") \
METHOD (finish, "finish", "()V") \
METHOD (setRequestedOrientation,"setRequestedOrientation", "(I)V") \
METHOD (getClipboardContent, "getClipboardContent", "()Ljava/lang/String;") \
METHOD (setClipboardContent, "setClipboardContent", "(Ljava/lang/String;)V") \
METHOD (excludeClipRegion, "excludeClipRegion", "(Landroid/graphics/Canvas;FFFF)V") \
@@ -276,6 +277,11 @@ extern AndroidSystem android;
METHOD (showOkCancelBox, "showOkCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \
METHOD (showYesNoCancelBox, "showYesNoCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \
STATICMETHOD (getLocaleValue, "getLocaleValue", "(Z)Ljava/lang/String;") \
STATICMETHOD (getDocumentsFolder, "getDocumentsFolder", "()Ljava/lang/String;") \
STATICMETHOD (getPicturesFolder, "getPicturesFolder", "()Ljava/lang/String;") \
STATICMETHOD (getMusicFolder, "getMusicFolder", "()Ljava/lang/String;") \
STATICMETHOD (getDownloadsFolder, "getDownloadsFolder", "()Ljava/lang/String;") \
STATICMETHOD (getMoviesFolder, "getMoviesFolder", "()Ljava/lang/String;") \
METHOD (scanFile, "scanFile", "(Ljava/lang/String;)V") \
METHOD (getTypeFaceFromAsset, "getTypeFaceFromAsset", "(Ljava/lang/String;)Landroid/graphics/Typeface;") \
METHOD (getTypeFaceFromByteArray,"getTypeFaceFromByteArray","([B)Landroid/graphics/Typeface;") \
@@ -287,7 +293,10 @@ extern AndroidSystem android;
METHOD (audioManagerGetProperty, "audioManagerGetProperty", "(Ljava/lang/String;)Ljava/lang/String;") \
METHOD (setCurrentThreadPriority, "setCurrentThreadPriority", "(I)I") \
METHOD (hasSystemFeature, "hasSystemFeature", "(Ljava/lang/String;)Z" ) \
METHOD (createNewThread, "createNewThread", "(J)Ljava/lang/Thread;") \
METHOD (createNewThread, "createNewThread", "(JLjava/lang/String;J)Ljava/lang/Thread;") \
METHOD (requestRuntimePermission, "requestRuntimePermission", "(IJ)V" ) \
METHOD (isPermissionGranted, "isPermissionGranted", "(I)Z" ) \
METHOD (isPermissionDeclaredInManifest, "isPermissionDeclaredInManifest", "(I)Z" ) \
DECLARE_JNI_CLASS (JuceAppActivity, JUCE_ANDROID_ACTIVITY_CLASSPATH);
#undef JNI_CLASS_MEMBERS


+ 90
- 0
source/modules/juce_core/native/juce_android_RuntimePermissions.cpp View File

@@ -0,0 +1,90 @@
/*
==============================================================================
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
==============================================================================
*/
namespace
{
void handleAndroidCallback (bool permissionWasGranted, RuntimePermissions::Callback* callbackPtr)
{
if (callbackPtr == nullptr)
{
// got a nullptr passed in from java! this should never happen...
jassertfalse;
return;
}
std::unique_ptr<RuntimePermissions::Callback> uptr (callbackPtr);
(*uptr) (permissionWasGranted);
}
}
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME,
androidRuntimePermissionsCallback,
void, (JNIEnv* env, jobject /*javaObjectHandle*/, jboolean permissionsGranted, jlong callbackPtr))
{
setEnv (env);
handleAndroidCallback (permissionsGranted != 0,
reinterpret_cast<RuntimePermissions::Callback*> (callbackPtr));
}
void RuntimePermissions::request (PermissionID permission, Callback callback)
{
if (! android.activity.callBooleanMethod (JuceAppActivity.isPermissionDeclaredInManifest, (jint) permission))
{
// Error! If you want to be able to request this runtime permission, you
// also need to declare it in your app's manifest. You can do so via
// the Introjucer. Otherwise this can't work.
jassertfalse;
callback (false);
return;
}
if (JUCE_ANDROID_API_VERSION < 23)
{
// There is no runtime permission system on API level below 23. As long as the
// permission is in the manifest (seems to be the case), we can simply ask Android
// if the app has the permission, and then directly call through to the callback.
callback (isGranted (permission));
return;
}
// we need to move the callback object to the heap so Java can keep track of the pointer
// and asynchronously pass it back to us (to be called and then deleted)
Callback* callbackPtr = new Callback (std::move (callback));
android.activity.callVoidMethod (JuceAppActivity.requestRuntimePermission, permission, (jlong) callbackPtr);
}
bool RuntimePermissions::isRequired (PermissionID /*permission*/)
{
return JUCE_ANDROID_API_VERSION >= 23;
}
bool RuntimePermissions::isGranted (PermissionID permission)
{
return android.activity.callBooleanMethod (JuceAppActivity.isPermissionGranted, permission);
}

+ 7
- 8
source/modules/juce_core/native/juce_android_Threads.cpp View File

@@ -84,10 +84,8 @@ struct AndroidThreadData
void JUCE_API juce_threadEntryPoint (void*);
extern "C" void* threadEntryProc (void*);
extern "C" void* threadEntryProc (void* userData)
void* threadEntryProc (AndroidThreadData* priv)
{
ScopedPointer<AndroidThreadData> priv (reinterpret_cast<AndroidThreadData*> (userData));
priv->tId = (Thread::ThreadID) pthread_self();
priv->eventSet.signal();
priv->eventGet.wait (-1);
@@ -100,12 +98,10 @@ extern "C" void* threadEntryProc (void* userData)
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024JuceThread), runThread,
void, (JNIEnv* env, jobject device, jlong host))
{
// Java may create a Midi thread which JUCE doesn't know about and this callback may be
// received on this thread. Java will have already created a JNI Env for this new thread,
// which we need to tell Juce about
// This thread does not have a JNIEnv assigned to it yet. So assign it now.
setEnv (env);
if (Thread* thread = reinterpret_cast<Thread*> (host))
if (AndroidThreadData* thread = reinterpret_cast<AndroidThreadData*> (host))
threadEntryProc (thread);
}
@@ -114,8 +110,11 @@ void Thread::launchThread()
threadHandle = 0;
ScopedPointer<AndroidThreadData> threadPrivateData = new AndroidThreadData (this);
const LocalRef<jstring> jName (javaString (threadName));
jobject juceNewThread = android.activity.callObjectMethod (JuceAppActivity.createNewThread, (jlong) threadPrivateData.get());
jobject juceNewThread = android.activity.callObjectMethod (JuceAppActivity.createNewThread,
(jlong) threadPrivateData.get(),
jName.get(), (jlong) threadStackSize);
if (jobject juceThread = getEnv()->NewGlobalRef (juceNewThread))
{


+ 1
- 1
source/modules/juce_core/native/juce_linux_CommonFile.cpp View File

@@ -63,7 +63,7 @@ static String getLinkedFile (const String& file)
HeapBlock<char> buffer (8194);
const int numBytes = (int) readlink (file.toRawUTF8(), buffer, 8192);
return String::fromUTF8 (buffer, jmax (0, numBytes));
};
}
bool File::isSymbolicLink() const
{


+ 3
- 0
source/modules/juce_core/native/juce_linux_SystemStats.cpp View File

@@ -156,7 +156,10 @@ void CPUInformation::initialise() noexcept
hasSSE3 = flags.contains ("sse3");
has3DNow = flags.contains ("3dnow");
hasSSSE3 = flags.contains ("ssse3");
hasSSE41 = flags.contains ("sse4_1");
hasSSE42 = flags.contains ("sse4_2");
hasAVX = flags.contains ("avx");
hasAVX2 = flags.contains ("avx2");
numCpus = LinuxStatsHelpers::getCpuInfo ("processor").getIntValue() + 1;
}


+ 8
- 2
source/modules/juce_core/native/juce_mac_Strings.mm View File

@@ -45,8 +45,14 @@ String String::fromCFString (CFStringRef cfString)
CFStringRef String::toCFString() const
{
const char* const utf8 = toRawUTF8();
return CFStringCreateWithBytes (kCFAllocatorDefault, (const UInt8*) utf8,
(CFIndex) strlen (utf8), kCFStringEncodingUTF8, false);
if (CFStringRef result = CFStringCreateWithBytes (kCFAllocatorDefault, (const UInt8*) utf8,
(CFIndex) strlen (utf8), kCFStringEncodingUTF8, false))
return result;
// If CFStringCreateWithBytes fails, it probably means there was a UTF8 format
// error, so we'll return an empty string rather than a null pointer.
return String().toCFString();
}
String String::convertToPrecomposedUnicode() const


+ 5
- 15
source/modules/juce_core/native/juce_mac_SystemStats.mm View File

@@ -81,27 +81,17 @@ void CPUInformation::initialise() noexcept
has3DNow = (b & (1u << 31)) != 0;
hasSSE3 = (c & (1u << 0)) != 0;
hasSSSE3 = (c & (1u << 9)) != 0;
hasSSE41 = (c & (1u << 20)) != 0;
hasSSE42 = (c & (1u << 19)) != 0;
hasAVX = (c & (1u << 28)) != 0;
SystemStatsHelpers::doCPUID (a, b, c, d, 7);
hasAVX2 = (b & (1u << 5)) != 0;
#endif
numCpus = (int) [[NSProcessInfo processInfo] activeProcessorCount];
}
#if JUCE_MAC
struct RLimitInitialiser
{
RLimitInitialiser()
{
rlimit lim;
getrlimit (RLIMIT_NOFILE, &lim);
lim.rlim_cur = lim.rlim_max = RLIM_INFINITY;
setrlimit (RLIMIT_NOFILE, &lim);
}
};
static RLimitInitialiser rLimitInitialiser;
#endif
//==============================================================================
#if ! JUCE_IOS
static String getOSXVersion()


+ 61
- 7
source/modules/juce_core/native/juce_posix_SharedCode.h View File

@@ -55,6 +55,7 @@ WaitableEvent::WaitableEvent (const bool useManualReset) noexcept
pthread_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT);
#endif
pthread_mutex_init (&mutex, &atts);
pthread_mutexattr_destroy (&atts);
}
WaitableEvent::~WaitableEvent() noexcept
@@ -149,6 +150,45 @@ void JUCE_CALLTYPE Process::terminate()
#endif
}
#if JUCE_MAC || JUCE_LINUX
bool Process::setMaxNumberOfFileHandles (int newMaxNumber) noexcept
{
rlimit lim;
if (getrlimit (RLIMIT_NOFILE, &lim) == 0)
{
if (newMaxNumber <= 0 && lim.rlim_cur == RLIM_INFINITY && lim.rlim_max == RLIM_INFINITY)
return true;
if (lim.rlim_cur >= (rlim_t) newMaxNumber)
return true;
}
lim.rlim_cur = lim.rlim_max = newMaxNumber <= 0 ? RLIM_INFINITY : (rlim_t) newMaxNumber;
return setrlimit (RLIMIT_NOFILE, &lim) == 0;
}
struct MaxNumFileHandlesInitialiser
{
MaxNumFileHandlesInitialiser() noexcept
{
#ifndef JUCE_PREFERRED_MAX_FILE_HANDLES
enum { JUCE_PREFERRED_MAX_FILE_HANDLES = 8192 };
#endif
// Try to give our app a decent number of file handles by default
if (! Process::setMaxNumberOfFileHandles (0))
{
for (int num = JUCE_PREFERRED_MAX_FILE_HANDLES; num > 256; num -= 1024)
if (Process::setMaxNumberOfFileHandles (num))
break;
}
}
};
static MaxNumFileHandlesInitialiser maxNumFileHandlesInitialiser;
#endif
//==============================================================================
const juce_wchar File::separator = '/';
const String File::separatorString ("/");
@@ -365,7 +405,7 @@ bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64
bool File::deleteFile() const
{
if (! exists())
if (! exists() && ! isSymbolicLink())
return true;
if (isDirectory())
@@ -395,7 +435,7 @@ Result File::createDirectoryInternal (const String& fileName) const
return getResultForReturnValue (mkdir (fileName.toUTF8(), 0777));
}
//=====================================================================
//==============================================================================
int64 juce_fileSetPosition (void* handle, int64 pos)
{
if (handle != 0 && lseek (getFD (handle), pos, SEEK_SET) == pos)
@@ -885,13 +925,25 @@ void Thread::launchThread()
{
threadHandle = 0;
pthread_t handle = 0;
pthread_attr_t attr;
pthread_attr_t* attrPtr = nullptr;
if (pthread_create (&handle, 0, threadEntryProc, this) == 0)
if (pthread_attr_init (&attr) == 0)
{
attrPtr = &attr;
pthread_attr_setstacksize (attrPtr, threadStackSize);
}
if (pthread_create (&handle, attrPtr, threadEntryProc, this) == 0)
{
pthread_detach (handle);
threadHandle = (void*) handle;
threadId = (ThreadID) threadHandle;
}
if (attrPtr != nullptr)
pthread_attr_destroy (attrPtr);
}
void Thread::closeThreadHandle()
@@ -1029,10 +1081,12 @@ public:
ActiveProcess (const StringArray& arguments, int streamFlags)
: childPID (0), pipeHandle (0), readHandle (0)
{
String exe (arguments[0].unquoted());
// Looks like you're trying to launch a non-existent exe or a folder (perhaps on OSX
// you're trying to launch the .app folder rather than the actual binary inside it?)
jassert ((! arguments[0].containsChar ('/'))
|| File::getCurrentWorkingDirectory().getChildFile (arguments[0]).existsAsFile());
jassert (File::getCurrentWorkingDirectory().getChildFile (exe).existsAsFile()
|| ! exe.containsChar (File::separator));
int pipeHandles[2] = { 0 };
@@ -1041,7 +1095,7 @@ public:
Array<char*> argv;
for (int i = 0; i < arguments.size(); ++i)
if (arguments[i].isNotEmpty())
argv.add (const_cast<char*> (arguments[i].toUTF8().getAddress()));
argv.add (const_cast<char*> (arguments[i].toRawUTF8()));
argv.add (nullptr);
@@ -1075,7 +1129,7 @@ public:
close (pipeHandles[1]);
#endif
if (execvp (argv[0], argv.getRawDataPointer()) < 0)
if (execvp (exe.toRawUTF8(), argv.getRawDataPointer()))
_exit (-1);
}
else


+ 16
- 7
source/modules/juce_core/native/juce_win32_Registry.cpp View File

@@ -31,13 +31,7 @@ struct RegistryKeyWrapper
RegistryKeyWrapper (String name, const bool createForWriting, const DWORD wow64Flags)
: key (0), wideCharValueName (nullptr)
{
HKEY rootKey = 0;
if (name.startsWithIgnoreCase ("HKEY_CURRENT_USER\\")) rootKey = HKEY_CURRENT_USER;
else if (name.startsWithIgnoreCase ("HKEY_LOCAL_MACHINE\\")) rootKey = HKEY_LOCAL_MACHINE;
else if (name.startsWithIgnoreCase ("HKEY_CLASSES_ROOT\\")) rootKey = HKEY_CLASSES_ROOT;
if (rootKey != 0)
if (HKEY rootKey = getRootKey (name))
{
name = name.substring (name.indexOfChar ('\\') + 1);
@@ -63,6 +57,21 @@ struct RegistryKeyWrapper
RegCloseKey (key);
}
static HKEY getRootKey (const String& name) noexcept
{
if (name.startsWithIgnoreCase ("HKEY_CURRENT_USER\\")) return HKEY_CURRENT_USER;
if (name.startsWithIgnoreCase ("HKCU\\")) return HKEY_CURRENT_USER;
if (name.startsWithIgnoreCase ("HKEY_LOCAL_MACHINE\\")) return HKEY_LOCAL_MACHINE;
if (name.startsWithIgnoreCase ("HKLM\\")) return HKEY_LOCAL_MACHINE;
if (name.startsWithIgnoreCase ("HKEY_CLASSES_ROOT\\")) return HKEY_CLASSES_ROOT;
if (name.startsWithIgnoreCase ("HKCR\\")) return HKEY_CLASSES_ROOT;
if (name.startsWithIgnoreCase ("HKEY_USERS\\")) return HKEY_USERS;
if (name.startsWithIgnoreCase ("HKU\\")) return HKEY_USERS;
jassertfalse; // The name starts with an unknown root key (or maybe an old Win9x type)
return 0;
}
static bool setValue (const String& regValuePath, const DWORD type,
const void* data, size_t dataSize, const DWORD wow64Flags)
{


+ 8
- 2
source/modules/juce_core/native/juce_win32_SystemStats.cpp View File

@@ -58,7 +58,7 @@ static void callCPUID (int result[4], int infoType)
__try
#endif
{
#if JUCE_GCC
#if JUCE_GCC || JUCE_CLANG
__asm__ __volatile__ ("cpuid" : "=a" (result[0]), "=b" (result[1]), "=c" (result[2]),"=d" (result[3]) : "a" (infoType));
#else
__asm
@@ -107,8 +107,14 @@ void CPUInformation::initialise() noexcept
hasSSE3 = (info[2] & (1 << 0)) != 0;
hasAVX = (info[2] & (1 << 28)) != 0;
hasSSSE3 = (info[2] & (1 << 9)) != 0;
hasSSE41 = (info[2] & (1 << 19)) != 0;
hasSSE42 = (info[2] & (1 << 20)) != 0;
has3DNow = (info[1] & (1 << 31)) != 0;
callCPUID (info, 7);
hasAVX2 = (info[1] & (1 << 5)) != 0;
SYSTEM_INFO systemInfo;
GetNativeSystemInfo (&systemInfo);
numCpus = (int) systemInfo.dwNumberOfProcessors;
@@ -325,7 +331,7 @@ static int64 juce_getClockCycleCounter() noexcept
// MS intrinsics version...
return (int64) __rdtsc();
#elif JUCE_GCC
#elif JUCE_GCC || JUCE_CLANG
// GNU inline asm version...
unsigned int hi = 0, lo = 0;


+ 2
- 1
source/modules/juce_core/native/juce_win32_Threads.cpp View File

@@ -109,7 +109,8 @@ static unsigned int __stdcall threadEntryProc (void* userData)
void Thread::launchThread()
{
unsigned int newThreadId;
threadHandle = (void*) _beginthreadex (0, 0, &threadEntryProc, this, 0, &newThreadId);
threadHandle = (void*) _beginthreadex (0, (unsigned int) threadStackSize,
&threadEntryProc, this, 0, &newThreadId);
threadId = (ThreadID) (pointer_sized_int) newThreadId;
}


+ 41
- 43
source/modules/juce_core/network/juce_Socket.cpp View File

@@ -62,6 +62,11 @@ namespace SocketHelpers
#endif
}
inline bool isValidPortNumber (int port) noexcept
{
return isPositiveAndBelow (port, 65536);
}
template <typename Type>
static bool setOption (const SocketHandle handle, int mode, int property, Type value) noexcept
{
@@ -115,6 +120,7 @@ namespace SocketHelpers
{
// unblock any pending read requests
::shutdown (h, SHUT_RDWR);
{
// see man-page of recv on linux about a race condition where the
// shutdown command is lost if the receiving thread does not have
@@ -133,37 +139,32 @@ namespace SocketHelpers
#endif
}
static bool bindSocket (const SocketHandle handle, const int port, const String& address) noexcept
static bool bindSocket (const SocketHandle handle, int port, const String& address) noexcept
{
if (handle <= 0 || port < 0)
if (handle <= 0 || ! isValidPortNumber (port))
return false;
struct sockaddr_in servTmpAddr;
zerostruct (servTmpAddr); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct)
servTmpAddr.sin_family = PF_INET;
servTmpAddr.sin_addr.s_addr = htonl (INADDR_ANY);
servTmpAddr.sin_port = htons ((uint16) port);
struct sockaddr_in addr;
zerostruct (addr); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct)
#if JUCE_WINDOWS
if (address.isNotEmpty())
servTmpAddr.sin_addr.s_addr = ::inet_addr (address.toUTF8());
#else
ignoreUnused (address);
#endif
addr.sin_family = PF_INET;
addr.sin_port = htons ((uint16) port);
addr.sin_addr.s_addr = address.isNotEmpty() ? ::inet_addr (address.toRawUTF8())
: htonl (INADDR_ANY);
return bind (handle, (struct sockaddr*) &servTmpAddr, sizeof (struct sockaddr_in)) >= 0;
return ::bind (handle, (struct sockaddr*) &addr, sizeof (addr)) >= 0;
}
static int getBoundPort (const SocketHandle handle) noexcept
{
if (handle <= 0)
return -1;
struct sockaddr_in sin_addr;
socklen_t len = sizeof (sin_addr);
if (handle > 0)
{
struct sockaddr_in addr;
socklen_t len = sizeof (addr);
if (getsockname (handle, (struct sockaddr*) &sin_addr, &len) == 0)
return ntohs (sin_addr.sin_port);
if (getsockname (handle, (struct sockaddr*) &addr, &len) == 0)
return ntohs (addr.sin_port);
}
return -1;
}
@@ -384,11 +385,11 @@ namespace SocketHelpers
struct ip_mreq mreq;
zerostruct (mreq);
mreq.imr_multiaddr.s_addr = inet_addr (multicastIPAddress.toUTF8());
mreq.imr_multiaddr.s_addr = inet_addr (multicastIPAddress.toRawUTF8());
mreq.imr_interface.s_addr = INADDR_ANY;
if (interfaceIPAddress.isNotEmpty())
mreq.imr_interface.s_addr = inet_addr (interfaceIPAddress.toUTF8());
mreq.imr_interface.s_addr = inet_addr (interfaceIPAddress.toRawUTF8());
return setsockopt (handle, IPPROTO_IP,
join ? IP_ADD_MEMBERSHIP
@@ -414,6 +415,8 @@ StreamingSocket::StreamingSocket (const String& host, int portNum, int h)
connected (true),
isListener (false)
{
jassert (SocketHelpers::isValidPortNumber (portNum));
SocketHelpers::initSockets();
SocketHelpers::resetSocketOptions (h, false, false);
}
@@ -455,6 +458,8 @@ bool StreamingSocket::bindToPort (const int port)
bool StreamingSocket::bindToPort (const int port, const String& addr)
{
jassert (SocketHelpers::isValidPortNumber (port));
return SocketHelpers::bindSocket (handle, port, addr);
}
@@ -467,6 +472,8 @@ bool StreamingSocket::connect (const String& remoteHostName,
const int remotePortNumber,
const int timeOutMillisecs)
{
jassert (SocketHelpers::isValidPortNumber (remotePortNumber));
if (isListener)
{
jassertfalse; // a listener socket can't connect to another one!
@@ -505,6 +512,8 @@ void StreamingSocket::close()
//==============================================================================
bool StreamingSocket::createListener (const int newPortNumber, const String& localHostName)
{
jassert (SocketHelpers::isValidPortNumber (newPortNumber));
if (connected)
close();
@@ -512,17 +521,6 @@ bool StreamingSocket::createListener (const int newPortNumber, const String& loc
portNumber = newPortNumber;
isListener = true;
struct sockaddr_in servTmpAddr;
zerostruct (servTmpAddr);
servTmpAddr.sin_family = PF_INET;
servTmpAddr.sin_addr.s_addr = htonl (INADDR_ANY);
if (localHostName.isNotEmpty())
servTmpAddr.sin_addr.s_addr = ::inet_addr (localHostName.toUTF8());
servTmpAddr.sin_port = htons ((uint16) portNumber);
handle = (int) socket (AF_INET, SOCK_STREAM, 0);
if (handle < 0)
@@ -532,15 +530,15 @@ bool StreamingSocket::createListener (const int newPortNumber, const String& loc
SocketHelpers::makeReusable (handle);
#endif
if (bind (handle, (struct sockaddr*) &servTmpAddr, sizeof (struct sockaddr_in)) < 0
|| listen (handle, SOMAXCONN) < 0)
if (SocketHelpers::bindSocket (handle, portNumber, localHostName)
&& listen (handle, SOMAXCONN) >= 0)
{
close();
return false;
connected = true;
return true;
}
connected = true;
return true;
close();
return false;
}
StreamingSocket* StreamingSocket::waitForNextConnection() const
@@ -614,14 +612,12 @@ bool DatagramSocket::bindToPort (const int port)
bool DatagramSocket::bindToPort (const int port, const String& addr)
{
if (handle < 0)
return false;
jassert (SocketHelpers::isValidPortNumber (port));
if (SocketHelpers::bindSocket (handle, port, addr))
{
isBound = true;
lastBindAddress = addr;
return true;
}
@@ -673,6 +669,8 @@ int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock
int DatagramSocket::write (const String& remoteHostname, int remotePortNumber,
const void* sourceBuffer, int numBytesToWrite)
{
jassert (SocketHelpers::isValidPortNumber (remotePortNumber));
if (handle < 0)
return -1;


+ 12
- 3
source/modules/juce_core/system/juce_PlatformDefs.h View File

@@ -109,7 +109,7 @@
#endif
//==============================================================================
#if JUCE_DEBUG || DOXYGEN
#if (JUCE_DEBUG && ! JUCE_DISABLE_ASSERTIONS) || DOXYGEN
/** Writes a string to the standard error stream.
Note that as well as a single string, you can use this to write multiple items
as a stream, e.g.
@@ -290,7 +290,7 @@
#elif JUCE_MSVC && ! JUCE_NO_DEPRECATION_WARNINGS
#define JUCE_DEPRECATED(functionDef) __declspec(deprecated) functionDef
#define JUCE_DEPRECATED_WITH_BODY(functionDef, body) __declspec(deprecated) functionDef body
#elif JUCE_GCC && ! JUCE_NO_DEPRECATION_WARNINGS
#elif (JUCE_GCC || JUCE_CLANG) && ! JUCE_NO_DEPRECATION_WARNINGS
#define JUCE_DEPRECATED(functionDef) functionDef __attribute__ ((deprecated))
#define JUCE_DEPRECATED_WITH_BODY(functionDef, body) functionDef __attribute__ ((deprecated)) body
#else
@@ -308,10 +308,19 @@
#endif
//==============================================================================
#if JUCE_GCC
#if JUCE_GCC || JUCE_CLANG
#define JUCE_PACKED __attribute__((packed))
#elif ! DOXYGEN
#define JUCE_PACKED
#endif
//==============================================================================
#if JUCE_GCC || DOXYGEN
/** This can be appended to a function declaration to tell gcc to disable associative
math optimisations which break some floating point algorithms. */
#define JUCE_NO_ASSOCIATIVE_MATH_OPTIMISATIONS __attribute__((__optimize__("no-associative-math")))
#else
#define JUCE_NO_ASSOCIATIVE_MATH_OPTIMISATIONS
#endif
#endif // JUCE_PLATFORMDEFS_H_INCLUDED

+ 6
- 2
source/modules/juce_core/system/juce_SystemStats.cpp View File

@@ -68,7 +68,8 @@ struct CPUInformation
CPUInformation() noexcept
: numCpus (0), hasMMX (false), hasSSE (false),
hasSSE2 (false), hasSSE3 (false), has3DNow (false),
hasSSSE3 (false), hasAVX (false)
hasSSSE3 (false), hasSSE41 (false), hasSSE42 (false),
hasAVX (false), hasAVX2 (false)
{
initialise();
}
@@ -76,7 +77,7 @@ struct CPUInformation
void initialise() noexcept;
int numCpus;
bool hasMMX, hasSSE, hasSSE2, hasSSE3, has3DNow, hasSSSE3, hasAVX;
bool hasMMX, hasSSE, hasSSE2, hasSSE3, has3DNow, hasSSSE3, hasSSE41, hasSSE42, hasAVX, hasAVX2;
};
static const CPUInformation& getCPUInformation() noexcept
@@ -92,7 +93,10 @@ bool SystemStats::hasSSE() noexcept { return getCPUInformation().hasSS
bool SystemStats::hasSSE2() noexcept { return getCPUInformation().hasSSE2; }
bool SystemStats::hasSSE3() noexcept { return getCPUInformation().hasSSE3; }
bool SystemStats::hasSSSE3() noexcept { return getCPUInformation().hasSSSE3; }
bool SystemStats::hasSSE41() noexcept { return getCPUInformation().hasSSE41; }
bool SystemStats::hasSSE42() noexcept { return getCPUInformation().hasSSE42; }
bool SystemStats::hasAVX() noexcept { return getCPUInformation().hasAVX; }
bool SystemStats::hasAVX2() noexcept { return getCPUInformation().hasAVX2; }
//==============================================================================


+ 10
- 7
source/modules/juce_core/system/juce_SystemStats.h View File

@@ -152,13 +152,16 @@ public:
*/
static String getCpuVendor();
static bool hasMMX() noexcept; /**< Returns true if Intel MMX instructions are available. */
static bool has3DNow() noexcept; /**< Returns true if AMD 3DNOW instructions are available. */
static bool hasSSE() noexcept; /**< Returns true if Intel SSE instructions are available. */
static bool hasSSE2() noexcept; /**< Returns true if Intel SSE2 instructions are available. */
static bool hasSSE3() noexcept; /**< Returns true if Intel SSE2 instructions are available. */
static bool hasSSSE3() noexcept; /**< Returns true if Intel SSSE3 instructions are available. */
static bool hasAVX() noexcept; /**< Returns true if Intel AVX instructions are available. */
static bool hasMMX() noexcept; /**< Returns true if Intel MMX instructions are available. */
static bool has3DNow() noexcept; /**< Returns true if AMD 3DNOW instructions are available. */
static bool hasSSE() noexcept; /**< Returns true if Intel SSE instructions are available. */
static bool hasSSE2() noexcept; /**< Returns true if Intel SSE2 instructions are available. */
static bool hasSSE3() noexcept; /**< Returns true if Intel SSE3 instructions are available. */
static bool hasSSSE3() noexcept; /**< Returns true if Intel SSSE3 instructions are available. */
static bool hasSSE41() noexcept; /**< Returns true if Intel SSE4.1 instructions are available. */
static bool hasSSE42() noexcept; /**< Returns true if Intel SSE4.2 instructions are available. */
static bool hasAVX() noexcept; /**< Returns true if Intel AVX instructions are available. */
static bool hasAVX2() noexcept; /**< Returns true if Intel AVX2 instructions are available. */
//==============================================================================
/** Finds out how much RAM is in the machine.


+ 1
- 6
source/modules/juce_core/system/juce_TargetPlatform.h View File

@@ -39,7 +39,7 @@
- Either JUCE_32BIT or JUCE_64BIT, depending on the architecture.
- Either JUCE_LITTLE_ENDIAN or JUCE_BIG_ENDIAN.
- Either JUCE_INTEL or JUCE_PPC
- Either JUCE_GCC or JUCE_MSVC
- Either JUCE_GCC or JUCE_CLANG or JUCE_MSVC
*/
//==============================================================================
@@ -61,12 +61,8 @@
#elif defined (LINUX) || defined (__linux__)
#define JUCE_LINUX 1
#elif defined (__APPLE_CPP__) || defined(__APPLE_CC__)
#define Point CarbonDummyPointName // (workaround to avoid definition of "Point" by old Carbon headers)
#define Component CarbonDummyCompName
#include <CoreFoundation/CoreFoundation.h> // (needed to find out what platform we're using)
#include "../native/juce_mac_ClangBugWorkaround.h"
#undef Point
#undef Component
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
#define JUCE_IPHONE 1
@@ -183,7 +179,6 @@
#ifdef __clang__
#define JUCE_CLANG 1
#define JUCE_GCC 1
#elif defined (__GNUC__)
#define JUCE_GCC 1
#elif defined (_MSC_VER)


+ 3
- 0
source/modules/juce_core/text/juce_CharPointer_UTF8.h View File

@@ -520,6 +520,9 @@ public:
return false;
}
if (numExtraValues == 0)
return false;
maxBytesToRead -= numExtraValues;
if (maxBytesToRead < 0)
return false;


+ 9
- 0
source/modules/juce_core/threads/juce_Process.h View File

@@ -144,6 +144,15 @@ public:
static void setDockIconVisible (bool isVisible);
#endif
#if JUCE_MAC || JUCE_LINUX || DOXYGEN
//==============================================================================
/** UNIX ONLY - Attempts to use setrlimit to change the maximum number of file
handles that the app can open. Pass 0 or less as the parameter to mean
'infinite'. Returns true if it succeeds.
*/
static bool setMaxNumberOfFileHandles (int maxNumberOfFiles) noexcept;
#endif
private:
Process();
JUCE_DECLARE_NON_COPYABLE (Process)


+ 2
- 1
source/modules/juce_core/threads/juce_Thread.cpp View File

@@ -26,11 +26,12 @@
==============================================================================
*/
Thread::Thread (const String& threadName_)
Thread::Thread (const String& threadName_, const size_t stackSize)
: threadName (threadName_),
threadHandle (nullptr),
threadId (0),
threadPriority (5),
threadStackSize (stackSize),
affinityMask (0),
shouldExit (false)
{


+ 8
- 1
source/modules/juce_core/threads/juce_Thread.h View File

@@ -53,8 +53,14 @@ public:
When first created, the thread is not running. Use the startThread()
method to start it.
@param threadName The name of the thread which typically appears in
debug logs and profiles.
@param threadStackSize The size of the stack of the thread. If this value
is zero then the default stack size of the OS will
be used.
*/
explicit Thread (const String& threadName);
explicit Thread (const String& threadName, size_t threadStackSize = 0);
/** Destructor.
@@ -270,6 +276,7 @@ private:
CriticalSection startStopLock;
WaitableEvent startSuspensionEvent, defaultEvent;
int threadPriority;
size_t threadStackSize;
uint32 affinityMask;
bool volatile shouldExit;


+ 311
- 95
source/modules/juce_core/time/juce_Time.cpp View File

@@ -28,55 +28,64 @@
namespace TimeHelpers
{
static struct tm millisToLocal (const int64 millis) noexcept
static std::tm millisToLocal (int64 millis) noexcept
{
struct tm result;
const int64 seconds = millis / 1000;
#if JUCE_WINDOWS && JUCE_MINGW
time_t now = (time_t) (millis / 1000);
return *localtime (&now);
if (seconds < 86400LL || seconds >= 2145916800LL)
{
// use extended maths for dates beyond 1970 to 2037..
const int timeZoneAdjustment = 31536000 - (int) (Time (1971, 0, 1, 0, 0).toMilliseconds() / 1000);
const int64 jdm = seconds + timeZoneAdjustment + 210866803200LL;
const int days = (int) (jdm / 86400LL);
const int a = 32044 + days;
const int b = (4 * a + 3) / 146097;
const int c = a - (b * 146097) / 4;
const int d = (4 * c + 3) / 1461;
const int e = c - (d * 1461) / 4;
const int m = (5 * e + 2) / 153;
result.tm_mday = e - (153 * m + 2) / 5 + 1;
result.tm_mon = m + 2 - 12 * (m / 10);
result.tm_year = b * 100 + d - 6700 + (m / 10);
result.tm_wday = (days + 1) % 7;
result.tm_yday = -1;
int t = (int) (jdm % 86400LL);
result.tm_hour = t / 3600;
t %= 3600;
result.tm_min = t / 60;
result.tm_sec = t % 60;
result.tm_isdst = -1;
}
else
{
time_t now = static_cast<time_t> (seconds);
#if JUCE_WINDOWS && JUCE_MINGW
return *localtime (&now);
#elif JUCE_WINDOWS
if (now >= 0 && now <= 0x793406fff)
localtime_s (&result, &now);
else
zerostruct (result);
#else
localtime_r (&now, &result); // more thread-safe
#endif
}
#elif JUCE_WINDOWS
std::tm result;
millis /= 1000;
if (_localtime64_s (&result, &millis) != 0)
zerostruct (result);
return result;
#else
std::tm result;
time_t now = (time_t) (millis / 1000);
if (localtime_r (&now, &result) == nullptr)
zerostruct (result);
return result;
#endif
}
static std::tm millisToUTC (int64 millis) noexcept
{
#if JUCE_WINDOWS && JUCE_MINGW
time_t now = (time_t) (millis / 1000);
return *gmtime (&now);
#elif JUCE_WINDOWS
std::tm result;
millis /= 1000;
if (_gmtime64_s (&result, &millis) != 0)
zerostruct (result);
return result;
#else
std::tm result;
time_t now = (time_t) (millis / 1000);
if (gmtime_r (&now, &result) == nullptr)
zerostruct (result);
return result;
#endif
}
static int getUTCOffsetSeconds (const int64 millis) noexcept
{
std::tm utc = millisToUTC (millis);
utc.tm_isdst = -1; // Treat this UTC time as local to find the offset
return (int) ((millis / 1000) - (int64) mktime (&utc));
}
static int extendedModulo (const int64 value, const int modulo) noexcept
@@ -85,7 +94,7 @@ namespace TimeHelpers
: (value - ((value / modulo) + 1) * modulo));
}
static inline String formatString (const String& format, const struct tm* const tm)
static inline String formatString (const String& format, const std::tm* const tm)
{
#if JUCE_ANDROID
typedef CharPointer_UTF8 StringType;
@@ -95,17 +104,23 @@ namespace TimeHelpers
typedef CharPointer_UTF32 StringType;
#endif
#ifdef JUCE_MSVC
if (tm->tm_year < -1900 || tm->tm_year > 8099)
return String(); // Visual Studio's library can only handle 0 -> 9999 AD
#endif
for (size_t bufferSize = 256; ; bufferSize += 256)
{
HeapBlock<StringType::CharType> buffer (bufferSize);
#if JUCE_ANDROID
const size_t numChars = strftime (buffer, bufferSize - 1, format.toUTF8(), tm);
#elif JUCE_WINDOWS
const size_t numChars = wcsftime (buffer, bufferSize - 1, format.toWideCharPointer(), tm);
#else
const size_t numChars = wcsftime (buffer, bufferSize - 1, format.toUTF32(), tm);
#endif
const size_t numChars =
#if JUCE_ANDROID
strftime (buffer, bufferSize - 1, format.toUTF8(), tm);
#elif JUCE_WINDOWS
wcsftime (buffer, bufferSize - 1, format.toWideCharPointer(), tm);
#else
wcsftime (buffer, bufferSize - 1, format.toUTF32(), tm);
#endif
if (numChars > 0 || format.isEmpty())
return String (StringType (buffer),
@@ -113,22 +128,70 @@ namespace TimeHelpers
}
}
//==============================================================================
static inline bool isLeapYear (int year) noexcept
{
return (year % 400 == 0) || ((year % 100 != 0) && (year % 4 == 0));
}
static inline int daysFromJan1 (int year, int month) noexcept
{
const short dayOfYear[] = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335,
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
return dayOfYear [(isLeapYear (year) ? 12 : 0) + month];
}
static inline int64 daysFromYear0 (int year) noexcept
{
return 365 * (year - 1) + (year / 400) - (year / 100) + (year / 4);
}
static inline int64 daysFrom1970 (int year) noexcept
{
return daysFromYear0 (year) - daysFromYear0 (1970);
}
static inline int64 daysFrom1970 (int year, int month) noexcept
{
if (month > 11)
{
year += month / 12;
month %= 12;
}
else if (month < 0)
{
const int numYears = (11 - month) / 12;
year -= numYears;
month += 12 * numYears;
}
return daysFrom1970 (year) + daysFromJan1 (year, month);
}
// There's no posix function that does a UTC version of mktime,
// so annoyingly we need to implement this manually..
static inline int64 mktime_utc (const std::tm& t) noexcept
{
return 24 * 3600 * (daysFrom1970 (t.tm_year + 1900, t.tm_mon) + (t.tm_mday - 1))
+ 3600 * t.tm_hour
+ 60 * t.tm_min
+ t.tm_sec;
}
static uint32 lastMSCounterValue = 0;
}
//==============================================================================
Time::Time() noexcept
: millisSinceEpoch (0)
Time::Time() noexcept : millisSinceEpoch (0)
{
}
Time::Time (const Time& other) noexcept
: millisSinceEpoch (other.millisSinceEpoch)
Time::Time (const Time& other) noexcept : millisSinceEpoch (other.millisSinceEpoch)
{
}
Time::Time (const int64 ms) noexcept
: millisSinceEpoch (ms)
Time::Time (const int64 ms) noexcept : millisSinceEpoch (ms)
{
}
@@ -141,41 +204,26 @@ Time::Time (const int year,
const int milliseconds,
const bool useLocalTime) noexcept
{
jassert (year > 100); // year must be a 4-digit version
if (year < 1971 || year >= 2038 || ! useLocalTime)
std::tm t;
t.tm_year = year - 1900;
t.tm_mon = month;
t.tm_mday = day;
t.tm_hour = hours;
t.tm_min = minutes;
t.tm_sec = seconds;
t.tm_isdst = -1;
const int64 time = useLocalTime ? (int64) mktime (&t)
: TimeHelpers::mktime_utc (t);
if (time >= 0)
{
// use extended maths for dates beyond 1970 to 2037..
const int timeZoneAdjustment = useLocalTime ? (31536000 - (int) (Time (1971, 0, 1, 0, 0).toMilliseconds() / 1000))
: 0;
const int a = (13 - month) / 12;
const int y = year + 4800 - a;
const int jd = day + (153 * (month + 12 * a - 2) + 2) / 5
+ (y * 365) + (y / 4) - (y / 100) + (y / 400)
- 32045;
const int64 s = ((int64) jd) * 86400LL - 210866803200LL;
millisSinceEpoch = 1000 * (s + (hours * 3600 + minutes * 60 + seconds - timeZoneAdjustment))
+ milliseconds;
millisSinceEpoch = 1000 * time + milliseconds;
}
else
{
struct tm t;
t.tm_year = year - 1900;
t.tm_mon = month;
t.tm_mday = day;
t.tm_hour = hours;
t.tm_min = minutes;
t.tm_sec = seconds;
t.tm_isdst = -1;
millisSinceEpoch = 1000 * (int64) mktime (&t);
if (millisSinceEpoch < 0)
millisSinceEpoch = 0;
else
millisSinceEpoch += milliseconds;
jassertfalse; // trying to create a date that is beyond the range that mktime supports!
millisSinceEpoch = 0;
}
}
@@ -315,7 +363,7 @@ String Time::toString (const bool includeDate,
String Time::formatted (const String& format) const
{
struct tm t (TimeHelpers::millisToLocal (millisSinceEpoch));
std::tm t (TimeHelpers::millisToLocal (millisSinceEpoch));
return TimeHelpers::formatString (format, &t);
}
@@ -388,6 +436,117 @@ String Time::getTimeZone() const noexcept
return zone[0].substring (0, 3);
}
int Time::getUTCOffsetSeconds() const noexcept
{
return TimeHelpers::getUTCOffsetSeconds (millisSinceEpoch);
}
String Time::getUTCOffsetString (bool includeSemiColon) const
{
if (int seconds = getUTCOffsetSeconds())
{
const int minutes = seconds / 60;
return String::formatted (includeSemiColon ? "%+03d:%02d"
: "%+03d%02d",
minutes / 60,
minutes % 60);
}
return "Z";
}
String Time::toISO8601 (bool includeDividerCharacters) const
{
return String::formatted (includeDividerCharacters ? "%04d-%02d-%02dT%02d:%02d:%02.03f"
: "%04d%02d%02dT%02d%02d%02.03f",
getYear(),
getMonth() + 1,
getDayOfMonth(),
getHours(),
getMinutes(),
getSeconds() + getMilliseconds() / 1000.0)
+ getUTCOffsetString (includeDividerCharacters);
}
static int parseFixedSizeIntAndSkip (String::CharPointerType& t, int numChars, char charToSkip) noexcept
{
int n = 0;
for (int i = numChars; --i >= 0;)
{
const int digit = (int) (*t - '0');
if (! isPositiveAndBelow (digit, 10))
return -1;
++t;
n = n * 10 + digit;
}
if (charToSkip != 0 && *t == (juce_wchar) charToSkip)
++t;
return n;
}
Time Time::fromISO8601 (StringRef iso) noexcept
{
String::CharPointerType t = iso.text;
const int year = parseFixedSizeIntAndSkip (t, 4, '-');
if (year < 0)
return Time();
const int month = parseFixedSizeIntAndSkip (t, 2, '-');
if (month < 0)
return Time();
const int day = parseFixedSizeIntAndSkip (t, 2, 0);
if (day < 0)
return Time();
int hours = 0, minutes = 0, milliseconds = 0;
if (*t == 'T')
{
++t;
hours = parseFixedSizeIntAndSkip (t, 2, ':');
if (hours < 0)
return Time();
minutes = parseFixedSizeIntAndSkip (t, 2, ':');
if (minutes < 0)
return Time();
milliseconds = (int) (1000.0 * CharacterFunctions::readDoubleValue (t));
}
const juce_wchar nextChar = t.getAndAdvance();
if (nextChar == '-' || nextChar == '+')
{
const int offsetHours = parseFixedSizeIntAndSkip (t, 2, ':');
if (offsetHours < 0)
return Time();
const int offsetMinutes = parseFixedSizeIntAndSkip (t, 2, 0);
if (offsetMinutes < 0)
return Time();
const int offsetMs = (offsetHours * 60 + offsetMinutes) * 60 * 1000;
milliseconds += nextChar == '-' ? offsetMs : -offsetMs; // NB: this seems backwards but is correct!
}
else if (nextChar != 0 && nextChar != 'Z')
{
return Time();
}
Time result (year, month - 1, day, hours, minutes, 0, 0, false);
result.millisSinceEpoch += milliseconds;
return result;
}
String Time::getMonthName (const bool threeLetterVersion) const
{
return getMonthName (getMonth(), threeLetterVersion);
@@ -442,19 +601,76 @@ static int getMonthNumberForCompileDate (const String& m) noexcept
if (m.equalsIgnoreCase (shortMonthNames[i]))
return i;
// If you hit this because your compiler has a non-standard __DATE__ format,
// let me know so we can add support for it!
// If you hit this because your compiler has an unusual __DATE__
// format, let us know so we can add support for it!
jassertfalse;
return 0;
}
Time Time::getCompilationDate()
{
StringArray dateTokens;
StringArray dateTokens, timeTokens;
dateTokens.addTokens (__DATE__, true);
dateTokens.removeEmptyStrings (true);
timeTokens.addTokens (__TIME__, ":", StringRef());
return Time (dateTokens[2].getIntValue(),
getMonthNumberForCompileDate (dateTokens[0]),
dateTokens[1].getIntValue(), 12, 0);
dateTokens[1].getIntValue(),
timeTokens[0].getIntValue(),
timeTokens[1].getIntValue());
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class TimeTests : public UnitTest
{
public:
TimeTests() : UnitTest ("Time") {}
void runTest() override
{
beginTest ("Time");
Time t = Time::getCurrentTime();
expect (t > Time());
Thread::sleep (15);
expect (Time::getCurrentTime() > t);
expect (t.getTimeZone().isNotEmpty());
expect (t.getUTCOffsetString (true) == "Z" || t.getUTCOffsetString (true).length() == 6);
expect (t.getUTCOffsetString (false) == "Z" || t.getUTCOffsetString (false).length() == 5);
expect (Time::fromISO8601 (t.toISO8601 (true)) == t);
expect (Time::fromISO8601 (t.toISO8601 (false)) == t);
expect (Time::fromISO8601 ("2016-02-16") == Time (2016, 1, 16, 0, 0, 0, 0, false));
expect (Time::fromISO8601 ("20160216Z") == Time (2016, 1, 16, 0, 0, 0, 0, false));
expect (Time::fromISO8601 ("2016-02-16T15:03:57+00:00") == Time (2016, 1, 16, 15, 3, 57, 0, false));
expect (Time::fromISO8601 ("20160216T150357+0000") == Time (2016, 1, 16, 15, 3, 57, 0, false));
expect (Time::fromISO8601 ("2016-02-16T15:03:57.999+00:00") == Time (2016, 1, 16, 15, 3, 57, 999, false));
expect (Time::fromISO8601 ("20160216T150357.999+0000") == Time (2016, 1, 16, 15, 3, 57, 999, false));
expect (Time::fromISO8601 ("2016-02-16T15:03:57.999Z") == Time (2016, 1, 16, 15, 3, 57, 999, false));
expect (Time::fromISO8601 ("20160216T150357.999Z") == Time (2016, 1, 16, 15, 3, 57, 999, false));
expect (Time::fromISO8601 ("2016-02-16T15:03:57.999-02:30") == Time (2016, 1, 16, 17, 33, 57, 999, false));
expect (Time::fromISO8601 ("20160216T150357.999-0230") == Time (2016, 1, 16, 17, 33, 57, 999, false));
expect (Time (1982, 1, 1, 12, 0, 0, 0, true) + RelativeTime::days (365) == Time (1983, 1, 1, 12, 0, 0, 0, true));
expect (Time (1970, 1, 1, 12, 0, 0, 0, true) + RelativeTime::days (365) == Time (1971, 1, 1, 12, 0, 0, 0, true));
expect (Time (2038, 1, 1, 12, 0, 0, 0, true) + RelativeTime::days (365) == Time (2039, 1, 1, 12, 0, 0, 0, true));
expect (Time (1982, 1, 1, 12, 0, 0, 0, false) + RelativeTime::days (365) == Time (1983, 1, 1, 12, 0, 0, 0, false));
expect (Time (1970, 1, 1, 12, 0, 0, 0, false) + RelativeTime::days (365) == Time (1971, 1, 1, 12, 0, 0, 0, false));
expect (Time (2038, 1, 1, 12, 0, 0, 0, false) + RelativeTime::days (365) == Time (2039, 1, 1, 12, 0, 0, 0, false));
}
};
static TimeTests timeTests;
#endif

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

Loading…
Cancel
Save