@@ -116,8 +116,11 @@ void AudioSampleBuffer::setDataToReferTo (float** dataToReferTo, | |||
jassert (dataToReferTo != nullptr); | |||
jassert (newNumChannels >= 0 && newNumSamples >= 0); | |||
allocatedBytes = 0; | |||
allocatedData.free(); | |||
if (allocatedBytes != 0) | |||
{ | |||
allocatedBytes = 0; | |||
allocatedData.free(); | |||
} | |||
numChannels = newNumChannels; | |||
size = newNumSamples; | |||
@@ -49,8 +49,13 @@ namespace FloatVectorHelpers | |||
{ | |||
typedef float Type; | |||
typedef __m128 ParallelType; | |||
typedef __m128 IntegerType; | |||
enum { numParallel = 4 }; | |||
// Integer and parallel types are the same for SSE. On neon they have different types | |||
static forcedinline IntegerType toint (ParallelType v) noexcept { return v; } | |||
static forcedinline ParallelType toflt (IntegerType v) noexcept { return v; } | |||
static forcedinline ParallelType load1 (Type v) noexcept { return _mm_load1_ps (&v); } | |||
static forcedinline ParallelType loadA (const Type* v) noexcept { return _mm_load_ps (v); } | |||
static forcedinline ParallelType loadU (const Type* v) noexcept { return _mm_loadu_ps (v); } | |||
@@ -63,6 +68,11 @@ namespace FloatVectorHelpers | |||
static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return _mm_max_ps (a, b); } | |||
static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return _mm_min_ps (a, b); } | |||
static forcedinline ParallelType bit_and (ParallelType a, ParallelType b) noexcept { return _mm_and_ps (a, b); } | |||
static forcedinline ParallelType bit_not (ParallelType a, ParallelType b) noexcept { return _mm_andnot_ps (a, b); } | |||
static forcedinline ParallelType bit_or (ParallelType a, ParallelType b) noexcept { return _mm_or_ps (a, b); } | |||
static forcedinline ParallelType bit_xor (ParallelType a, ParallelType b) noexcept { return _mm_xor_ps (a, b); } | |||
static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1], v[2], v[3]); } | |||
static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1], v[2], v[3]); } | |||
}; | |||
@@ -71,8 +81,13 @@ namespace FloatVectorHelpers | |||
{ | |||
typedef double Type; | |||
typedef __m128d ParallelType; | |||
typedef __m128d IntegerType; | |||
enum { numParallel = 2 }; | |||
// Integer and parallel types are the same for SSE. On neon they have different types | |||
static forcedinline IntegerType toint (ParallelType v) noexcept { return v; } | |||
static forcedinline ParallelType toflt (IntegerType v) noexcept { return v; } | |||
static forcedinline ParallelType load1 (Type v) noexcept { return _mm_load1_pd (&v); } | |||
static forcedinline ParallelType loadA (const Type* v) noexcept { return _mm_load_pd (v); } | |||
static forcedinline ParallelType loadU (const Type* v) noexcept { return _mm_loadu_pd (v); } | |||
@@ -85,10 +100,17 @@ namespace FloatVectorHelpers | |||
static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return _mm_max_pd (a, b); } | |||
static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return _mm_min_pd (a, b); } | |||
static forcedinline ParallelType bit_and (ParallelType a, ParallelType b) noexcept { return _mm_and_pd (a, b); } | |||
static forcedinline ParallelType bit_not (ParallelType a, ParallelType b) noexcept { return _mm_andnot_pd (a, b); } | |||
static forcedinline ParallelType bit_or (ParallelType a, ParallelType b) noexcept { return _mm_or_pd (a, b); } | |||
static forcedinline ParallelType bit_xor (ParallelType a, ParallelType b) noexcept { return _mm_xor_pd (a, b); } | |||
static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1]); } | |||
static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1]); } | |||
}; | |||
#define JUCE_BEGIN_VEC_OP \ | |||
typedef FloatVectorHelpers::ModeType<sizeof(*dest)>::Mode Mode; \ | |||
if (FloatVectorHelpers::isSSE2Available()) \ | |||
@@ -126,23 +148,62 @@ namespace FloatVectorHelpers | |||
#define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \ | |||
JUCE_BEGIN_VEC_OP \ | |||
setupOp \ | |||
if (FloatVectorHelpers::isAligned (dest)) \ | |||
{ \ | |||
Mode::ParallelType (&loadSrc1) (const Mode::Type* v) = FloatVectorHelpers::isAligned (src1) ? Mode::loadA : Mode::loadU; \ | |||
Mode::ParallelType (&loadSrc2) (const Mode::Type* v) = FloatVectorHelpers::isAligned (src2) ? Mode::loadA : Mode::loadU; \ | |||
void (&storeDst) (Mode::Type* dest, Mode::ParallelType a) = FloatVectorHelpers::isAligned (dest) ? Mode::storeA : Mode::storeU; \ | |||
JUCE_VEC_LOOP_TWO_SOURCES (vecOp, loadSrc1, loadSrc2, storeDst, locals, increment); \ | |||
if (FloatVectorHelpers::isAligned (src1)) \ | |||
{ \ | |||
if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadA, Mode::loadA, Mode::storeA, locals, increment) \ | |||
else JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadA, Mode::loadU, Mode::storeA, locals, increment) \ | |||
} \ | |||
else \ | |||
{ \ | |||
if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadA, Mode::storeA, locals, increment) \ | |||
else JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadU, Mode::storeA, locals, increment) \ | |||
} \ | |||
} \ | |||
else \ | |||
{ \ | |||
if (FloatVectorHelpers::isAligned (src1)) \ | |||
{ \ | |||
if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadA, Mode::loadA, Mode::storeU, locals, increment) \ | |||
else JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadA, Mode::loadU, Mode::storeU, locals, increment) \ | |||
} \ | |||
else \ | |||
{ \ | |||
if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadA, Mode::storeU, locals, increment) \ | |||
else JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ | |||
} \ | |||
} \ | |||
JUCE_FINISH_VEC_OP (normalOp) | |||
#define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST(normalOp, vecOp, locals, increment, setupOp) \ | |||
JUCE_BEGIN_VEC_OP \ | |||
setupOp \ | |||
if (FloatVectorHelpers::isAligned (dest)) \ | |||
{ \ | |||
Mode::ParallelType (&loadSrc1) (const Mode::Type* v) = FloatVectorHelpers::isAligned (src1) ? Mode::loadA : Mode::loadU; \ | |||
Mode::ParallelType (&loadSrc2) (const Mode::Type* v) = FloatVectorHelpers::isAligned (src2) ? Mode::loadA : Mode::loadU; \ | |||
Mode::ParallelType (&loadDst) (const Mode::Type* v) = FloatVectorHelpers::isAligned (dest) ? Mode::loadA : Mode::loadU; \ | |||
void (&storeDst) (Mode::Type* dest, Mode::ParallelType a) = FloatVectorHelpers::isAligned (dest) ? Mode::storeA : Mode::storeU; \ | |||
JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, loadSrc1, loadSrc2, loadDst, storeDst, locals, increment); \ | |||
if (FloatVectorHelpers::isAligned (src1)) \ | |||
{ \ | |||
if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadA, Mode::loadA, Mode::loadA, Mode::storeA, locals, increment) \ | |||
else JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadA, Mode::loadU, Mode::loadA, Mode::storeA, locals, increment) \ | |||
} \ | |||
else \ | |||
{ \ | |||
if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadU, Mode::loadA, Mode::loadA, Mode::storeA, locals, increment) \ | |||
else JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadU, Mode::loadU, Mode::loadA, Mode::storeA, locals, increment) \ | |||
} \ | |||
} \ | |||
else \ | |||
{ \ | |||
if (FloatVectorHelpers::isAligned (src1)) \ | |||
{ \ | |||
if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadA, Mode::loadA, Mode::loadU, Mode::storeU, locals, increment) \ | |||
else JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadA, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ | |||
} \ | |||
else \ | |||
{ \ | |||
if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadU, Mode::loadA, Mode::loadU, Mode::storeU, locals, increment) \ | |||
else JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadU, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ | |||
} \ | |||
} \ | |||
JUCE_FINISH_VEC_OP (normalOp) | |||
@@ -154,8 +215,12 @@ namespace FloatVectorHelpers | |||
{ | |||
typedef float Type; | |||
typedef float32x4_t ParallelType; | |||
typedef uint32x4_t IntegerType; | |||
enum { numParallel = 4 }; | |||
static forcedinline IntegerType toint (ParallelType v) noexcept { union { ParallelType f; IntegerType i; } u; u.f = v; return u.i; } | |||
static forcedinline ParallelType toflt (IntegerType v) noexcept { union { ParallelType f; IntegerType i; } u; u.i = v; return u.f; } | |||
static forcedinline ParallelType load1 (Type v) noexcept { return vld1q_dup_f32 (&v); } | |||
static forcedinline ParallelType loadA (const Type* v) noexcept { return vld1q_f32 (v); } | |||
static forcedinline ParallelType loadU (const Type* v) noexcept { return vld1q_f32 (v); } | |||
@@ -168,6 +233,11 @@ namespace FloatVectorHelpers | |||
static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return vmaxq_f32 (a, b); } | |||
static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return vminq_f32 (a, b); } | |||
static forcedinline ParallelType bit_and (ParallelType a, ParallelType b) noexcept { return toflt (vandq_u32 (toint (a), toint (b))); } | |||
static forcedinline ParallelType bit_not (ParallelType a, ParallelType b) noexcept { return toflt (vbicq_u32 (toint (a), toint (b))); } | |||
static forcedinline ParallelType bit_or (ParallelType a, ParallelType b) noexcept { return toflt (vorrq_u32 (toint (a), toint (b))); } | |||
static forcedinline ParallelType bit_xor (ParallelType a, ParallelType b) noexcept { return toflt (veorq_u32 (toint (a), toint (b))); } | |||
static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1], v[2], v[3]); } | |||
static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1], v[2], v[3]); } | |||
}; | |||
@@ -176,8 +246,12 @@ namespace FloatVectorHelpers | |||
{ | |||
typedef double Type; | |||
typedef double ParallelType; | |||
typedef uint64 IntegerType; | |||
enum { numParallel = 1 }; | |||
static forcedinline IntegerType toint (ParallelType v) noexcept { union { ParallelType f; IntegerType i; } u; u.f = v; return u.i; } | |||
static forcedinline ParallelType toflt (IntegerType v) noexcept { union { ParallelType f; IntegerType i; } u; u.i = v; return u.f; } | |||
static forcedinline ParallelType load1 (Type v) noexcept { return v; } | |||
static forcedinline ParallelType loadA (const Type* v) noexcept { return *v; } | |||
static forcedinline ParallelType loadU (const Type* v) noexcept { return *v; } | |||
@@ -190,6 +264,11 @@ namespace FloatVectorHelpers | |||
static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return jmax (a, b); } | |||
static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return jmin (a, b); } | |||
static forcedinline ParallelType bit_and (ParallelType a, ParallelType b) noexcept { return toflt (toint (a) & toint (b)); } | |||
static forcedinline ParallelType bit_not (ParallelType a, ParallelType b) noexcept { return toflt ((~toint (a)) & toint (b)); } | |||
static forcedinline ParallelType bit_or (ParallelType a, ParallelType b) noexcept { return toflt (toint (a) | toint (b)); } | |||
static forcedinline ParallelType bit_xor (ParallelType a, ParallelType b) noexcept { return toflt (toint (a) ^ toint (b)); } | |||
static forcedinline Type max (ParallelType a) noexcept { return a; } | |||
static forcedinline Type min (ParallelType a) noexcept { return a; } | |||
}; | |||
@@ -428,7 +507,7 @@ void JUCE_CALLTYPE FloatVectorOperations::clear (float* dest, int num) noexcept | |||
#if JUCE_USE_VDSP_FRAMEWORK | |||
vDSP_vclr (dest, 1, (size_t) num); | |||
#else | |||
zeromem (dest, num * sizeof (float)); | |||
zeromem (dest, (size_t) num * sizeof (float)); | |||
#endif | |||
} | |||
@@ -437,7 +516,7 @@ void JUCE_CALLTYPE FloatVectorOperations::clear (double* dest, int num) noexcept | |||
#if JUCE_USE_VDSP_FRAMEWORK | |||
vDSP_vclrD (dest, 1, (size_t) num); | |||
#else | |||
zeromem (dest, num * sizeof (double)); | |||
zeromem (dest, (size_t) num * sizeof (double)); | |||
#endif | |||
} | |||
@@ -495,8 +574,12 @@ void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (double* dest, const | |||
void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float amount, int num) noexcept | |||
{ | |||
#if JUCE_USE_VDSP_FRAMEWORK | |||
vDSP_vsadd (dest, 1, &amount, dest, 1, (vDSP_Length) num); | |||
#else | |||
JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, Mode::add (d, amountToAdd), JUCE_LOAD_DEST, | |||
const Mode::ParallelType amountToAdd = Mode::load1 (amount);) | |||
#endif | |||
} | |||
void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, double amount, int num) noexcept | |||
@@ -601,9 +684,13 @@ void JUCE_CALLTYPE FloatVectorOperations::subtract (double* dest, const double* | |||
void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept | |||
{ | |||
#if JUCE_USE_VDSP_FRAMEWORK | |||
vDSP_vsma (src, 1, &multiplier, dest, 1, dest, 1, (vDSP_Length) num); | |||
#else | |||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)), | |||
JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, | |||
const Mode::ParallelType mult = Mode::load1 (multiplier);) | |||
#endif | |||
} | |||
void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept | |||
@@ -723,6 +810,33 @@ void FloatVectorOperations::negate (double* dest, const double* src, int num) no | |||
#endif | |||
} | |||
void FloatVectorOperations::abs (float* dest, const float* src, int num) noexcept | |||
{ | |||
#if JUCE_USE_VDSP_FRAMEWORK | |||
vDSP_vabs ((float*) src, 1, dest, 1, (vDSP_Length) num); | |||
#else | |||
union {float f; uint32 i;} signMask; | |||
signMask.i = 0x7fffffffUL; | |||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = fabsf (src[i]), Mode::bit_and (s, mask), | |||
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, | |||
const Mode::ParallelType mask = Mode::load1 (signMask.f);) | |||
#endif | |||
} | |||
void FloatVectorOperations::abs (double* dest, const double* src, int num) noexcept | |||
{ | |||
#if JUCE_USE_VDSP_FRAMEWORK | |||
vDSP_vabsD ((double*) src, 1, dest, 1, (vDSP_Length) num); | |||
#else | |||
union {double d; uint64 i;} signMask; | |||
signMask.i = 0x7fffffffffffffffULL; | |||
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = fabs (src[i]), Mode::bit_and (s, mask), | |||
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, | |||
const Mode::ParallelType mask = Mode::load1 (signMask.d);) | |||
#endif | |||
} | |||
void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, const int* src, float multiplier, int num) noexcept | |||
{ | |||
#if JUCE_USE_ARM_NEON | |||
@@ -966,6 +1080,12 @@ public: | |||
FloatVectorOperations::subtract (data1, data2, num); | |||
u.expect (areAllValuesEqual (data1, num, (ValueType) 512)); | |||
FloatVectorOperations::abs (data1, data2, num); | |||
u.expect (areAllValuesEqual (data1, num, (ValueType) 256)); | |||
FloatVectorOperations::abs (data2, data1, num); | |||
u.expect (areAllValuesEqual (data2, num, (ValueType) 256)); | |||
fillRandomly (random, int1, num); | |||
doConversionTest (u, data1, data2, int1, num); | |||
@@ -137,6 +137,12 @@ public: | |||
/** Copies a source vector to a destination, negating each value. */ | |||
static void JUCE_CALLTYPE negate (double* dest, const double* src, int numValues) noexcept; | |||
/** Copies a source vector to a destination, taking the absolute of each value. */ | |||
static void JUCE_CALLTYPE abs (float* dest, const float* src, int numValues) noexcept; | |||
/** Copies a source vector to a destination, taking the absolute of each value. */ | |||
static void JUCE_CALLTYPE abs (double* dest, const double* src, int numValues) noexcept; | |||
/** Converts a stream of integers to floats, multiplying each one by the given multiplier. */ | |||
static void JUCE_CALLTYPE convertFixedToFloat (float* dest, const int* src, float multiplier, int numValues) noexcept; | |||
@@ -333,7 +333,7 @@ private: | |||
if (countdown <= 0) | |||
currentValue = target; | |||
else | |||
step = (target - currentValue) / countdown; | |||
step = (target - currentValue) / (float) countdown; | |||
} | |||
} | |||
@@ -66,6 +66,9 @@ | |||
#if __ARM_NEON__ && ! (JUCE_USE_VDSP_FRAMEWORK || defined (JUCE_USE_ARM_NEON)) | |||
#define JUCE_USE_ARM_NEON 1 | |||
#endif | |||
#if JUCE_USE_ARM_NEON | |||
#include <arm_neon.h> | |||
#endif | |||
@@ -145,7 +145,7 @@ void MidiKeyboardState::processNextMidiBuffer (MidiBuffer& buffer, | |||
const bool injectIndirectEvents) | |||
{ | |||
MidiBuffer::Iterator i (buffer); | |||
MidiMessage message (0xf4, 0.0); | |||
MidiMessage message; | |||
int time; | |||
const ScopedLock sl (lock); | |||
@@ -63,6 +63,7 @@ void SynthesiserVoice::clearCurrentNote() | |||
} | |||
void SynthesiserVoice::aftertouchChanged (int) {} | |||
void SynthesiserVoice::channelPressureChanged (int) {} | |||
bool SynthesiserVoice::wasStartedBefore (const SynthesiserVoice& other) const noexcept | |||
{ | |||
@@ -73,6 +74,7 @@ bool SynthesiserVoice::wasStartedBefore (const SynthesiserVoice& other) const no | |||
Synthesiser::Synthesiser() | |||
: sampleRate (0), | |||
lastNoteOnCounter (0), | |||
minimumSubBlockSize (32), | |||
shouldStealNotes (true) | |||
{ | |||
for (int i = 0; i < numElementsInArray (lastPitchWheelValues); ++i) | |||
@@ -131,6 +133,12 @@ void Synthesiser::setNoteStealingEnabled (const bool shouldSteal) | |||
shouldStealNotes = shouldSteal; | |||
} | |||
void Synthesiser::setMinimumRenderingSubdivisionSize (int numSamples) noexcept | |||
{ | |||
jassert (numSamples > 0); // it wouldn't make much sense for this to be less than 1 | |||
minimumSubBlockSize = numSamples; | |||
} | |||
//============================================================================== | |||
void Synthesiser::setCurrentPlaybackSampleRate (const double newRate) | |||
{ | |||
@@ -153,30 +161,45 @@ void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBu | |||
// must set the sample rate before using this! | |||
jassert (sampleRate != 0); | |||
const ScopedLock sl (lock); | |||
MidiBuffer::Iterator midiIterator (midiData); | |||
midiIterator.setNextSamplePosition (startSample); | |||
MidiMessage m (0xf4, 0.0); | |||
int midiEventPos; | |||
MidiMessage m; | |||
const ScopedLock sl (lock); | |||
while (numSamples > 0) | |||
{ | |||
int midiEventPos; | |||
const bool useEvent = midiIterator.getNextEvent (m, midiEventPos) | |||
&& midiEventPos < startSample + numSamples; | |||
if (! midiIterator.getNextEvent (m, midiEventPos)) | |||
{ | |||
renderVoices (outputBuffer, startSample, numSamples); | |||
return; | |||
} | |||
const int numThisTime = useEvent ? midiEventPos - startSample | |||
: numSamples; | |||
const int samplesToNextMidiMessage = midiEventPos - startSample; | |||
if (numThisTime > 0) | |||
renderVoices (outputBuffer, startSample, numThisTime); | |||
if (samplesToNextMidiMessage >= numSamples) | |||
{ | |||
renderVoices (outputBuffer, startSample, numSamples); | |||
handleMidiEvent (m); | |||
break; | |||
} | |||
if (useEvent) | |||
if (samplesToNextMidiMessage < minimumSubBlockSize) | |||
{ | |||
handleMidiEvent (m); | |||
continue; | |||
} | |||
startSample += numThisTime; | |||
numSamples -= numThisTime; | |||
renderVoices (outputBuffer, startSample, samplesToNextMidiMessage); | |||
handleMidiEvent (m); | |||
startSample += samplesToNextMidiMessage; | |||
numSamples -= samplesToNextMidiMessage; | |||
} | |||
while (midiIterator.getNextEvent (m, midiEventPos)) | |||
handleMidiEvent (m); | |||
} | |||
void Synthesiser::renderVoices (AudioSampleBuffer& buffer, int startSample, int numSamples) | |||
@@ -187,33 +210,41 @@ void Synthesiser::renderVoices (AudioSampleBuffer& buffer, int startSample, int | |||
void Synthesiser::handleMidiEvent (const MidiMessage& m) | |||
{ | |||
const int channel = m.getChannel(); | |||
if (m.isNoteOn()) | |||
{ | |||
noteOn (m.getChannel(), m.getNoteNumber(), m.getFloatVelocity()); | |||
noteOn (channel, m.getNoteNumber(), m.getFloatVelocity()); | |||
} | |||
else if (m.isNoteOff()) | |||
{ | |||
noteOff (m.getChannel(), m.getNoteNumber(), m.getFloatVelocity(), true); | |||
noteOff (channel, m.getNoteNumber(), m.getFloatVelocity(), true); | |||
} | |||
else if (m.isAllNotesOff() || m.isAllSoundOff()) | |||
{ | |||
allNotesOff (m.getChannel(), true); | |||
allNotesOff (channel, true); | |||
} | |||
else if (m.isPitchWheel()) | |||
{ | |||
const int channel = m.getChannel(); | |||
const int wheelPos = m.getPitchWheelValue(); | |||
lastPitchWheelValues [channel - 1] = wheelPos; | |||
handlePitchWheel (channel, wheelPos); | |||
} | |||
else if (m.isAftertouch()) | |||
{ | |||
handleAftertouch (m.getChannel(), m.getNoteNumber(), m.getAfterTouchValue()); | |||
handleAftertouch (channel, m.getNoteNumber(), m.getAfterTouchValue()); | |||
} | |||
else if (m.isChannelPressure()) | |||
{ | |||
handleChannelPressure (channel, m.getChannelPressureValue()); | |||
} | |||
else if (m.isController()) | |||
{ | |||
handleController (m.getChannel(), m.getControllerNumber(), m.getControllerValue()); | |||
handleController (channel, m.getControllerNumber(), m.getControllerValue()); | |||
} | |||
else if (m.isProgramChange()) | |||
{ | |||
handleProgramChange (channel, m.getProgramChangeNumber()); | |||
} | |||
} | |||
@@ -375,6 +406,19 @@ void Synthesiser::handleAftertouch (int midiChannel, int midiNoteNumber, int aft | |||
} | |||
} | |||
void Synthesiser::handleChannelPressure (int midiChannel, int channelPressureValue) | |||
{ | |||
const ScopedLock sl (lock); | |||
for (int i = voices.size(); --i >= 0;) | |||
{ | |||
SynthesiserVoice* const voice = voices.getUnchecked (i); | |||
if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)) | |||
voice->channelPressureChanged (channelPressureValue); | |||
} | |||
} | |||
void Synthesiser::handleSustainPedal (int midiChannel, bool isDown) | |||
{ | |||
jassert (midiChannel > 0 && midiChannel <= 16); | |||
@@ -423,6 +467,12 @@ void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/) | |||
jassert (midiChannel > 0 && midiChannel <= 16); | |||
} | |||
void Synthesiser::handleProgramChange (int midiChannel, int programNumber) | |||
{ | |||
(void) midiChannel; (void) programNumber; | |||
jassert (midiChannel > 0 && midiChannel <= 16); | |||
} | |||
//============================================================================== | |||
SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, | |||
int midiChannel, int midiNoteNumber, | |||
@@ -481,10 +531,10 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, | |||
} | |||
} | |||
const int stealableVoiceRange = usableVoices.size() - 6; | |||
const int numUsableVoices = usableVoices.size(); | |||
// The oldest note that's playing with the target pitch playing is ideal.. | |||
for (int i = 0; i < stealableVoiceRange; ++i) | |||
for (int i = 0; i < numUsableVoices; ++i) | |||
{ | |||
SynthesiserVoice* const voice = usableVoices.getUnchecked (i); | |||
@@ -492,8 +542,27 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, | |||
return voice; | |||
} | |||
// ..otherwise, look for the oldest note that isn't the top or bottom note.. | |||
for (int i = 0; i < stealableVoiceRange; ++i) | |||
// Oldest voice that has been released (no finger on it and not held by sustain pedal) | |||
for (int i = 0; i < numUsableVoices; ++i) | |||
{ | |||
SynthesiserVoice* const voice = usableVoices.getUnchecked (i); | |||
if (voice != bottom && voice != top && ! voice->isKeyDown() && ! voice->isSostenutoPedalDown()) | |||
return voice; | |||
} | |||
// Oldest voice that doesn't have a finger on it: | |||
for (int i = 0; i < numUsableVoices; ++i) | |||
{ | |||
SynthesiserVoice* const voice = usableVoices.getUnchecked (i); | |||
if (voice != bottom && voice != top && ! voice->isKeyDown()) | |||
return voice; | |||
} | |||
// At this point, all notes have fingers on them, so look for the oldest note | |||
// that isn't the top or bottom note.. | |||
for (int i = 0; i < numUsableVoices; ++i) | |||
{ | |||
SynthesiserVoice* const voice = usableVoices.getUnchecked (i); | |||
@@ -501,6 +570,7 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, | |||
return voice; | |||
} | |||
// ..otherwise, there's only one or two voices to choose from - we'll return the oldest one.. | |||
return usableVoices.getFirst(); | |||
// ..otherwise, there's only one or two voices to choose from - prefer to steal the highest one: | |||
jassert (top != nullptr || bottom != nullptr); | |||
return (top == nullptr ? bottom : top); | |||
} |
@@ -161,6 +161,11 @@ public: | |||
*/ | |||
virtual void aftertouchChanged (int newAftertouchValue); | |||
/** Called to let the voice know that the channel pressure has changed. | |||
This will be called during the rendering callback, so must be fast and thread-safe. | |||
*/ | |||
virtual void channelPressureChanged (int newChannelPressureValue); | |||
//============================================================================== | |||
/** Renders the next block of data for this voice. | |||
@@ -440,6 +445,20 @@ public: | |||
*/ | |||
virtual void handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue); | |||
/** Sends a channel pressure message. | |||
This will send a channel pressure message to any voices that are playing sounds on | |||
the given midi channel. | |||
This method will be called automatically according to the midi data passed into | |||
renderNextBlock(), but may be called explicitly too. | |||
@param midiChannel the midi channel, from 1 to 16 inclusive | |||
@param channelPressureValue the pressure value, between 0 and 127, as returned | |||
by MidiMessage::getChannelPressureValue() | |||
*/ | |||
virtual void handleChannelPressure (int midiChannel, int channelPressureValue); | |||
/** Handles a sustain pedal event. */ | |||
virtual void handleSustainPedal (int midiChannel, bool isDown); | |||
@@ -449,6 +468,13 @@ public: | |||
/** Can be overridden to handle soft pedal events. */ | |||
virtual void handleSoftPedal (int midiChannel, bool isDown); | |||
/** Can be overridden to handle an incoming program change message. | |||
The base class implementation of this has no effect, but you may want to make your | |||
own synth react to program changes. | |||
*/ | |||
virtual void handleProgramChange (int midiChannel, | |||
int programNumber); | |||
//============================================================================== | |||
/** Tells the synthesiser what the sample rate is for the audio it's being used to render. | |||
@@ -479,6 +505,22 @@ public: | |||
*/ | |||
double getSampleRate() const noexcept { return sampleRate; } | |||
/** 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 | |||
into smaller blocks that lie between all the incoming midi messages, and it is these smaller | |||
sub-blocks that are rendered with multiple calls to renderVoices(). | |||
Obviously in a pathological case where there are midi messages on every sample, then | |||
renderVoices() could be called once per sample and lead to poor performance, so this | |||
setting allows you to set a lower limit on the block size. | |||
The default setting is 32, which means that midi messages are accurate to about < 1ms | |||
accuracy, which is probably fine for most purposes, but you may want to increase or | |||
decrease this value for your synth. | |||
*/ | |||
void setMinimumRenderingSubdivisionSize (int numSamples) noexcept; | |||
protected: | |||
//============================================================================== | |||
/** This is used to control access to the rendering callback and the note trigger methods. */ | |||
@@ -537,6 +579,7 @@ private: | |||
//============================================================================== | |||
double sampleRate; | |||
uint32 lastNoteOnCounter; | |||
int minimumSubBlockSize; | |||
bool shouldStealNotes; | |||
BigInteger sustainPedalsDown; | |||
@@ -551,6 +551,11 @@ double AudioDeviceManager::chooseBestSampleRate (double rate) const | |||
const Array<double> rates (currentAudioDevice->getAvailableSampleRates()); | |||
if (rate > 0 && rates.contains (rate)) | |||
return rate; | |||
rate = currentAudioDevice->getCurrentSampleRate(); | |||
if (rate > 0 && rates.contains (rate)) | |||
return rate; | |||
@@ -57,7 +57,7 @@ static void getDeviceSampleRates (snd_pcm_t* handle, Array<double>& rates) | |||
for (int i = 0; ratesToTry[i] != 0; ++i) | |||
{ | |||
if (snd_pcm_hw_params_any (handle, hwParams) >= 0 | |||
&& snd_pcm_hw_params_test_rate (handle, hwParams, ratesToTry[i], 0) == 0) | |||
&& snd_pcm_hw_params_test_rate (handle, hwParams, (unsigned int) ratesToTry[i], 0) == 0) | |||
{ | |||
rates.addIfNotAlreadyThere ((double) ratesToTry[i]); | |||
} | |||
@@ -257,10 +257,10 @@ public: | |||
int dir = 0; | |||
unsigned int periods = 4; | |||
snd_pcm_uframes_t samplesPerPeriod = bufferSize; | |||
snd_pcm_uframes_t samplesPerPeriod = (snd_pcm_uframes_t) bufferSize; | |||
if (JUCE_ALSA_FAILED (snd_pcm_hw_params_set_rate_near (handle, hwParams, &sampleRate, 0)) | |||
|| JUCE_ALSA_FAILED (snd_pcm_hw_params_set_channels (handle, hwParams, numChannels)) | |||
|| JUCE_ALSA_FAILED (snd_pcm_hw_params_set_channels (handle, hwParams, (unsigned int ) numChannels)) | |||
|| JUCE_ALSA_FAILED (snd_pcm_hw_params_set_periods_near (handle, hwParams, &periods, &dir)) | |||
|| JUCE_ALSA_FAILED (snd_pcm_hw_params_set_period_size_near (handle, hwParams, &samplesPerPeriod, &dir)) | |||
|| JUCE_ALSA_FAILED (snd_pcm_hw_params (handle, hwParams))) | |||
@@ -274,7 +274,7 @@ public: | |||
|| JUCE_ALSA_FAILED (snd_pcm_hw_params_get_periods (hwParams, &periods, &dir))) | |||
latency = 0; | |||
else | |||
latency = frames * (periods - 1); // (this is the method JACK uses to guess the latency..) | |||
latency = (int) frames * ((int) periods - 1); // (this is the method JACK uses to guess the latency..) | |||
JUCE_ALSA_LOG ("frames: " << (int) frames << ", periods: " << (int) periods | |||
<< ", samplesPerPeriod: " << (int) samplesPerPeriod); | |||
@@ -316,22 +316,22 @@ public: | |||
if (isInterleaved) | |||
{ | |||
scratch.ensureSize (sizeof (float) * numSamples * numChannelsRunning, false); | |||
scratch.ensureSize ((size_t) ((int) sizeof (float) * numSamples * numChannelsRunning), false); | |||
for (int i = 0; i < numChannelsRunning; ++i) | |||
converter->convertSamples (scratch.getData(), i, data[i], 0, numSamples); | |||
numDone = snd_pcm_writei (handle, scratch.getData(), numSamples); | |||
numDone = snd_pcm_writei (handle, scratch.getData(), (snd_pcm_uframes_t) numSamples); | |||
} | |||
else | |||
{ | |||
for (int i = 0; i < numChannelsRunning; ++i) | |||
converter->convertSamples (data[i], data[i], numSamples); | |||
numDone = snd_pcm_writen (handle, (void**) data, numSamples); | |||
numDone = snd_pcm_writen (handle, (void**) data, (snd_pcm_uframes_t) numSamples); | |||
} | |||
if (numDone < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, numDone, 1 /* silent */))) | |||
if (numDone < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, (int) numDone, 1 /* silent */))) | |||
return false; | |||
if (numDone < numSamples) | |||
@@ -347,12 +347,12 @@ public: | |||
if (isInterleaved) | |||
{ | |||
scratch.ensureSize (sizeof (float) * numSamples * numChannelsRunning, false); | |||
scratch.ensureSize ((size_t) ((int) sizeof (float) * numSamples * numChannelsRunning), false); | |||
scratch.fillWith (0); // (not clearing this data causes warnings in valgrind) | |||
snd_pcm_sframes_t num = snd_pcm_readi (handle, scratch.getData(), numSamples); | |||
snd_pcm_sframes_t num = snd_pcm_readi (handle, scratch.getData(), (snd_pcm_uframes_t) numSamples); | |||
if (num < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, num, 1 /* silent */))) | |||
if (num < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, (int) num, 1 /* silent */))) | |||
return false; | |||
if (num < numSamples) | |||
@@ -363,9 +363,9 @@ public: | |||
} | |||
else | |||
{ | |||
snd_pcm_sframes_t num = snd_pcm_readn (handle, (void**) data, numSamples); | |||
snd_pcm_sframes_t num = snd_pcm_readn (handle, (void**) data, (snd_pcm_uframes_t) numSamples); | |||
if (num < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, num, 1 /* silent */))) | |||
if (num < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, (int) num, 1 /* silent */))) | |||
return false; | |||
if (num < numSamples) | |||
@@ -503,7 +503,7 @@ public: | |||
} | |||
} | |||
ensureMinimumNumBitsSet (outputChannels, minChansOut); | |||
ensureMinimumNumBitsSet (outputChannels, (int) minChansOut); | |||
outputChannelBuffer.setSize (jmax ((int) minChansOut, outputChannels.getHighestBit()) + 1, bufferSize); | |||
outputChannelBuffer.clear(); | |||
@@ -557,7 +557,7 @@ public: | |||
return; | |||
} | |||
ensureMinimumNumBitsSet (currentInputChans, minChansIn); | |||
ensureMinimumNumBitsSet (currentInputChans, (int) minChansIn); | |||
if (! inputDevice->setParameters ((unsigned int) sampleRate, | |||
jlimit ((int) minChansIn, (int) maxChansIn, currentInputChans.getHighestBit() + 1), | |||
@@ -656,7 +656,7 @@ public: | |||
snd_pcm_sframes_t avail = snd_pcm_avail_update (inputDevice->handle); | |||
if (avail < 0) | |||
JUCE_ALSA_FAILED (snd_pcm_recover (inputDevice->handle, avail, 0)); | |||
JUCE_ALSA_FAILED (snd_pcm_recover (inputDevice->handle, (int) avail, 0)); | |||
} | |||
audioIoInProgress = true; | |||
@@ -688,7 +688,7 @@ public: | |||
else | |||
{ | |||
for (int i = 0; i < outputChannelDataForCallback.size(); ++i) | |||
zeromem (outputChannelDataForCallback[i], sizeof (float) * bufferSize); | |||
zeromem (outputChannelDataForCallback[i], sizeof (float) * (size_t) bufferSize); | |||
} | |||
} | |||
@@ -702,7 +702,7 @@ public: | |||
snd_pcm_sframes_t avail = snd_pcm_avail_update (outputDevice->handle); | |||
if (avail < 0) | |||
JUCE_ALSA_FAILED (snd_pcm_recover (outputDevice->handle, avail, 0)); | |||
JUCE_ALSA_FAILED (snd_pcm_recover (outputDevice->handle, (int) avail, 0)); | |||
audioIoInProgress = true; | |||
@@ -1092,9 +1092,9 @@ private: | |||
if (snd_ctl_pcm_next_device (handle, &device) < 0 || device < 0) | |||
break; | |||
snd_pcm_info_set_device (pcmInfo, device); | |||
snd_pcm_info_set_device (pcmInfo, (unsigned int) device); | |||
for (int subDevice = 0, nbSubDevice = 1; subDevice < nbSubDevice; ++subDevice) | |||
for (unsigned int subDevice = 0, nbSubDevice = 1; subDevice < nbSubDevice; ++subDevice) | |||
{ | |||
snd_pcm_info_set_subdevice (pcmInfo, subDevice); | |||
snd_pcm_info_set_stream (pcmInfo, SND_PCM_STREAM_CAPTURE); | |||
@@ -1118,7 +1118,7 @@ private: | |||
} | |||
else | |||
{ | |||
id << "hw:" << cardId << "," << device << "," << subDevice; | |||
id << "hw:" << cardId << "," << device << "," << (int) subDevice; | |||
name << cardName << ", " << snd_pcm_info_get_name (pcmInfo) | |||
<< " {" << snd_pcm_info_get_subdevice_name (pcmInfo) << "}"; | |||
} | |||
@@ -133,14 +133,14 @@ private: | |||
if (snd_midi_event_new (maxEventSize, &midiParser) >= 0) | |||
{ | |||
const int numPfds = snd_seq_poll_descriptors_count (seqHandle, POLLIN); | |||
HeapBlock<pollfd> pfd (numPfds); | |||
snd_seq_poll_descriptors (seqHandle, pfd, numPfds, POLLIN); | |||
HeapBlock<pollfd> pfd ((size_t) numPfds); | |||
snd_seq_poll_descriptors (seqHandle, pfd, (unsigned int) numPfds, POLLIN); | |||
HeapBlock <uint8> buffer (maxEventSize); | |||
while (! threadShouldExit()) | |||
{ | |||
if (poll (pfd, numPfds, 100) > 0) // there was a "500" here which is a bit long when we exit the program and have to wait for a timeout on this poll call | |||
if (poll (pfd, (nfds_t) numPfds, 100) > 0) // there was a "500" here which is a bit long when we exit the program and have to wait for a timeout on this poll call | |||
{ | |||
if (threadShouldExit()) | |||
break; | |||
@@ -154,14 +154,14 @@ private: | |||
if (snd_seq_event_input (seqHandle, &inputEvent) >= 0) | |||
{ | |||
// xxx what about SYSEXes that are too big for the buffer? | |||
const int numBytes = snd_midi_event_decode (midiParser, buffer, | |||
const long numBytes = snd_midi_event_decode (midiParser, buffer, | |||
maxEventSize, inputEvent); | |||
snd_midi_event_reset_decode (midiParser); | |||
if (numBytes > 0) | |||
{ | |||
const MidiMessage message ((const uint8*) buffer, numBytes, | |||
const MidiMessage message ((const uint8*) buffer, (int) numBytes, | |||
Time::getMillisecondCounter() * 0.001); | |||
client.handleIncomingMidiMessage (message, inputEvent->dest.port); | |||
@@ -410,7 +410,7 @@ public: | |||
maxEventSize (16 * 1024) | |||
{ | |||
jassert (port.isValid() && midiOutput != nullptr); | |||
snd_midi_event_new (maxEventSize, &midiParser); | |||
snd_midi_event_new ((size_t) maxEventSize, &midiParser); | |||
} | |||
~MidiOutputDevice() | |||
@@ -425,7 +425,7 @@ public: | |||
{ | |||
maxEventSize = message.getRawDataSize(); | |||
snd_midi_event_free (midiParser); | |||
snd_midi_event_new (maxEventSize, &midiParser); | |||
snd_midi_event_new ((size_t) maxEventSize, &midiParser); | |||
} | |||
snd_seq_event_t event; | |||
@@ -1067,6 +1067,12 @@ public: | |||
jassert (! isOpen()); | |||
jassert (! device->isOpen()); | |||
devices.add (new DeviceWrapper (*this, device, useInputs, useOutputs)); | |||
if (currentSampleRate == 0) | |||
currentSampleRate = device->getCurrentSampleRate(); | |||
if (currentBufferSize == 0) | |||
currentBufferSize = device->getCurrentBufferSizeSamples(); | |||
} | |||
Array<AudioIODevice*> getDevices() const | |||
@@ -1416,13 +1416,15 @@ private: | |||
} | |||
}; | |||
template <> | |||
struct ASIOCallbackFunctions <sizeof(currentASIODev) / sizeof(currentASIODev[0])> | |||
{ | |||
static void setCallbacksForDevice (ASIOCallbacks&, ASIOAudioIODevice*) noexcept {} | |||
}; | |||
void setCallbackFunctions() noexcept | |||
{ | |||
/**/ if (currentASIODev[0] == this) ASIOCallbackFunctions<0>::setCallbacks (callbacks); | |||
else if (currentASIODev[1] == this) ASIOCallbackFunctions<1>::setCallbacks (callbacks); | |||
else if (currentASIODev[2] == this) ASIOCallbackFunctions<2>::setCallbacks (callbacks); | |||
else if (currentASIODev[3] == this) ASIOCallbackFunctions<3>::setCallbacks (callbacks); | |||
else jassertfalse; | |||
ASIOCallbackFunctions<0>::setCallbacksForDevice (callbacks, this); | |||
} | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ASIOAudioIODevice) | |||
@@ -30,9 +30,9 @@ public: | |||
Writer (OutputStream* destStream, const String& formatName, | |||
const File& appFile, int vbr, int cbr, | |||
double sampleRate, unsigned int numberOfChannels, | |||
unsigned int bitsPerSample, const StringPairArray& metadata) | |||
int bitsPerSample, const StringPairArray& metadata) | |||
: AudioFormatWriter (destStream, formatName, sampleRate, | |||
numberOfChannels, bitsPerSample), | |||
numberOfChannels, (unsigned int) bitsPerSample), | |||
vbrLevel (vbr), cbrBitrate (cbr), | |||
tempWav (".wav") | |||
{ | |||
@@ -426,9 +426,8 @@ struct VBRTagData | |||
if (flags & 4) | |||
{ | |||
if (toc != nullptr) | |||
for (int i = 0; i < 100; ++i) | |||
toc[i] = data[i]; | |||
for (int i = 0; i < 100; ++i) | |||
toc[i] = data[i]; | |||
data += 100; | |||
} | |||
@@ -194,7 +194,7 @@ public: | |||
bufferList->mNumberBuffers = 1; | |||
bufferList->mBuffers[0].mNumberChannels = inputStreamDesc.mChannelsPerFrame; | |||
bufferList->mBuffers[0].mDataByteSize = jmax ((UInt32) 4096, (UInt32) (samplesPerFrame * inputStreamDesc.mBytesPerFrame) + 16); | |||
bufferList->mBuffers[0].mDataByteSize = jmax ((UInt32) 4096, (UInt32) (samplesPerFrame * (int) inputStreamDesc.mBytesPerFrame) + 16); | |||
dataBuffer.malloc (bufferList->mBuffers[0].mDataByteSize); | |||
bufferList->mBuffers[0].mData = dataBuffer; | |||
@@ -262,10 +262,10 @@ public: | |||
} | |||
int framesToDo = jmin (numSamples, (int) (bufferList->mBuffers[0].mDataByteSize / inputStreamDesc.mBytesPerFrame)); | |||
bufferList->mBuffers[0].mDataByteSize = inputStreamDesc.mBytesPerFrame * framesToDo; | |||
bufferList->mBuffers[0].mDataByteSize = inputStreamDesc.mBytesPerFrame * (UInt32) framesToDo; | |||
UInt32 outFlags = 0; | |||
UInt32 actualNumFrames = framesToDo; | |||
UInt32 actualNumFrames = (UInt32) framesToDo; | |||
OSStatus err = MovieAudioExtractionFillBuffer (extractor, &actualNumFrames, bufferList, &outFlags); | |||
if (err != noErr) | |||
{ | |||
@@ -274,7 +274,7 @@ public: | |||
} | |||
lastSampleRead = startSampleInFile + actualNumFrames; | |||
const int samplesReceived = actualNumFrames; | |||
const int samplesReceived = (int) actualNumFrames; | |||
for (int j = numDestChannels; --j >= 0;) | |||
{ | |||
@@ -298,7 +298,7 @@ public: | |||
{ | |||
for (int j = numDestChannels; --j >= 0;) | |||
if (destSamples[j] != nullptr) | |||
zeromem (destSamples[j] + startOffsetInDestBuffer, sizeof (int) * numSamples); | |||
zeromem (destSamples[j] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples); | |||
break; | |||
} | |||
@@ -609,16 +609,18 @@ namespace WavFileHelpers | |||
{ | |||
static MemoryBlock createFrom (const StringPairArray& values) | |||
{ | |||
const String s = values[WavAudioFormat::tracktionLoopInfo]; | |||
MemoryBlock data; | |||
MemoryOutputStream out; | |||
const String s (values[WavAudioFormat::tracktionLoopInfo]); | |||
if (s.isNotEmpty()) | |||
{ | |||
MemoryOutputStream os (data, false); | |||
os.writeString (s); | |||
out.writeString (s); | |||
if ((out.getDataSize() & 1) != 0) | |||
out.writeByte (0); | |||
} | |||
return data; | |||
return out.getMemoryBlock(); | |||
} | |||
}; | |||
@@ -850,7 +850,7 @@ int floor1_encode(oggpack_buffer *opb,vorbis_block *vb, | |||
/* generate the partition's first stage cascade value */ | |||
if(csubbits){ | |||
int maxval[8]; | |||
int maxval[8] = { 0 }; | |||
for(k=0;k<csub;k++){ | |||
int booknum=info->class_subbook[classx][k]; | |||
if(booknum<0){ | |||
@@ -119,7 +119,7 @@ public: | |||
should then be deleted by the caller. | |||
If the stream can't be created for some reason (e.g. the parameters passed in | |||
here aren't suitable), this will return 0. | |||
here aren't suitable), this will return nullptr. | |||
@param streamToWriteTo the stream that the data will go to - this will be | |||
deleted by the AudioFormatWriter object when it's no longer | |||
@@ -58,10 +58,10 @@ public: | |||
@see isLooping | |||
*/ | |||
void setLooping (bool shouldLoop); | |||
void setLooping (bool shouldLoop) override; | |||
/** Returns whether loop-mode is turned on or not. */ | |||
bool isLooping() const { return looping; } | |||
bool isLooping() const override { return looping; } | |||
/** Returns the reader that's being used. */ | |||
AudioFormatReader* getAudioFormatReader() const noexcept { return reader; } | |||
@@ -126,7 +126,7 @@ public: | |||
void startNote (int midiNoteNumber, float velocity, SynthesiserSound*, int pitchWheel) override; | |||
void stopNote (float velocity, bool allowTailOff) override; | |||
void pitchWheelMoved (int newValue); | |||
void pitchWheelMoved (int newValue) override; | |||
void controllerMoved (int controllerNumber, int newValue) override; | |||
void renderNextBlock (AudioSampleBuffer&, int startSample, int numSamples) override; | |||
@@ -184,7 +184,7 @@ namespace AudioUnitFormatHelpers | |||
const char* const utf8 = fileOrIdentifier.toUTF8(); | |||
if (CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, | |||
strlen (utf8), file.isDirectory())) | |||
(CFIndex) strlen (utf8), file.isDirectory())) | |||
{ | |||
CFBundleRef bundleRef = CFBundleCreate (kCFAllocatorDefault, url); | |||
CFRelease (url); | |||
@@ -345,8 +345,8 @@ public: | |||
refreshParameterList(); | |||
updateNumChannels(); | |||
producesMidiMessages = canProduceMidiOutput(); | |||
setPlayConfigDetails (numInputBusChannels * numInputBusses, | |||
numOutputBusChannels * numOutputBusses, | |||
setPlayConfigDetails ((int) (numInputBusChannels * numInputBusses), | |||
(int) (numOutputBusChannels * numOutputBusses), | |||
rate, blockSize); | |||
setLatencySamples (0); | |||
@@ -420,7 +420,7 @@ public: | |||
UInt32 sampleRateSize = sizeof (sampleRateIn); | |||
const Float64 sr = newSampleRate; | |||
for (int i = 0; i < numInputBusses; ++i) | |||
for (AudioUnitElement i = 0; i < numInputBusses; ++i) | |||
{ | |||
AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, i, &sampleRateIn, &sampleRateSize); | |||
@@ -428,7 +428,7 @@ public: | |||
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, i, &sr, sizeof (sr)); | |||
} | |||
for (int i = 0; i < numOutputBusses; ++i) | |||
for (AudioUnitElement i = 0; i < numOutputBusses; ++i) | |||
{ | |||
AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, i, &sampleRateOut, &sampleRateSize); | |||
@@ -440,9 +440,9 @@ public: | |||
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, | |||
&frameSize, sizeof (frameSize)); | |||
setPlayConfigDetails (numInputBusChannels * numInputBusses, | |||
numOutputBusChannels * numOutputBusses, | |||
newSampleRate, estimatedSamplesPerBlock); | |||
setPlayConfigDetails ((int) (numInputBusChannels * numInputBusses), | |||
(int) (numOutputBusChannels * numOutputBusses), | |||
(double) newSampleRate, estimatedSamplesPerBlock); | |||
Float64 latencySecs = 0.0; | |||
UInt32 latencySize = sizeof (latencySecs); | |||
@@ -463,13 +463,13 @@ public: | |||
stream.mBitsPerChannel = 32; | |||
stream.mChannelsPerFrame = numInputBusChannels; | |||
for (int i = 0; i < numInputBusses; ++i) | |||
for (AudioUnitElement i = 0; i < numInputBusses; ++i) | |||
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, | |||
kAudioUnitScope_Input, i, &stream, sizeof (stream)); | |||
stream.mChannelsPerFrame = numOutputBusChannels; | |||
for (int i = 0; i < numOutputBusses; ++i) | |||
for (AudioUnitElement i = 0; i < numOutputBusses; ++i) | |||
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, | |||
kAudioUnitScope_Output, i, &stream, sizeof (stream)); | |||
} | |||
@@ -518,8 +518,8 @@ public: | |||
void resetBusses() | |||
{ | |||
for (int i = 0; i < numInputBusses; ++i) AudioUnitReset (audioUnit, kAudioUnitScope_Input, i); | |||
for (int i = 0; i < numOutputBusses; ++i) AudioUnitReset (audioUnit, kAudioUnitScope_Output, i); | |||
for (AudioUnitElement i = 0; i < numInputBusses; ++i) AudioUnitReset (audioUnit, kAudioUnitScope_Input, i); | |||
for (AudioUnitElement i = 0; i < numOutputBusses; ++i) AudioUnitReset (audioUnit, kAudioUnitScope_Output, i); | |||
} | |||
void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override | |||
@@ -530,17 +530,17 @@ public: | |||
{ | |||
timeStamp.mHostTime = AudioGetCurrentHostTime(); | |||
for (int i = 0; i < numOutputBusses; ++i) | |||
for (AudioUnitElement i = 0; i < numOutputBusses; ++i) | |||
{ | |||
if (AudioBufferList* const abl = getAudioBufferListForBus(i)) | |||
{ | |||
abl->mNumberBuffers = numOutputBusChannels; | |||
for (int j = 0; j < numOutputBusChannels; ++j) | |||
for (AudioUnitElement j = 0; j < numOutputBusChannels; ++j) | |||
{ | |||
abl->mBuffers[j].mNumberChannels = 1; | |||
abl->mBuffers[j].mDataByteSize = sizeof (float) * numSamples; | |||
abl->mBuffers[j].mData = buffer.getWritePointer (i * numOutputBusChannels + j); | |||
abl->mBuffers[j].mDataByteSize = sizeof (float) * (size_t) numSamples; | |||
abl->mBuffers[j].mData = buffer.getWritePointer ((int) (i * numOutputBusChannels + j)); | |||
} | |||
} | |||
} | |||
@@ -557,19 +557,19 @@ public: | |||
if (midiEventSize <= 3) | |||
MusicDeviceMIDIEvent (audioUnit, | |||
midiEventData[0], midiEventData[1], midiEventData[2], | |||
midiEventPosition); | |||
(UInt32) midiEventPosition); | |||
else | |||
MusicDeviceSysEx (audioUnit, midiEventData, midiEventSize); | |||
MusicDeviceSysEx (audioUnit, midiEventData, (UInt32) midiEventSize); | |||
} | |||
midiMessages.clear(); | |||
} | |||
for (int i = 0; i < numOutputBusses; ++i) | |||
for (AudioUnitElement i = 0; i < numOutputBusses; ++i) | |||
{ | |||
AudioUnitRenderActionFlags flags = 0; | |||
AudioUnitRender (audioUnit, &flags, &timeStamp, i, numSamples, getAudioBufferListForBus (i)); | |||
AudioUnitRender (audioUnit, &flags, &timeStamp, i, (UInt32) numSamples, getAudioBufferListForBus (i)); | |||
} | |||
timeStamp.mSampleTime += numSamples; | |||
@@ -796,7 +796,7 @@ public: | |||
CFDataRef data = (CFDataRef) CFWriteStreamCopyProperty (stream, kCFStreamPropertyDataWritten); | |||
destData.setSize (bytesWritten); | |||
destData.setSize ((size_t) bytesWritten); | |||
destData.copyFrom (CFDataGetBytePtr (data), 0, destData.getSize()); | |||
CFRelease (data); | |||
@@ -911,7 +911,7 @@ private: | |||
HeapBlock <AudioBufferList> outputBufferList; | |||
AudioTimeStamp timeStamp; | |||
AudioSampleBuffer* currentBuffer; | |||
int numInputBusChannels, numOutputBusChannels, numInputBusses, numOutputBusses; | |||
AudioUnitElement numInputBusChannels, numOutputBusChannels, numInputBusses, numOutputBusses; | |||
AudioUnit audioUnit; | |||
AUEventListenerRef eventListenerRef; | |||
@@ -941,7 +941,7 @@ private: | |||
info.inputProcRefCon = this; | |||
info.inputProc = renderGetInputCallback; | |||
for (int i = 0; i < numInputBusses; ++i) | |||
for (AudioUnitElement i = 0; i < numInputBusses; ++i) | |||
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, | |||
kAudioUnitScope_Input, i, &info, sizeof (info)); | |||
} | |||
@@ -1028,11 +1028,11 @@ private: | |||
break; | |||
case kAudioUnitEvent_BeginParameterChangeGesture: | |||
beginParameterChangeGesture (event.mArgument.mParameter.mParameterID); | |||
beginParameterChangeGesture ((int) event.mArgument.mParameter.mParameterID); | |||
break; | |||
case kAudioUnitEvent_EndParameterChangeGesture: | |||
endParameterChangeGesture (event.mArgument.mParameter.mParameterID); | |||
endParameterChangeGesture ((int) event.mArgument.mParameter.mParameterID); | |||
break; | |||
default: | |||
@@ -1062,7 +1062,7 @@ private: | |||
for (UInt32 i = 0; i < ioData->mNumberBuffers; ++i) | |||
{ | |||
const int bufferChannel = inBusNumber * numInputBusChannels + i; | |||
const int bufferChannel = (int) (inBusNumber * numInputBusChannels + i); | |||
if (bufferChannel < currentBuffer->getNumChannels()) | |||
{ | |||
@@ -1131,16 +1131,16 @@ private: | |||
if (ph != nullptr && ph->getCurrentPosition (result)) | |||
{ | |||
setIfNotNull (outTimeSig_Numerator, result.timeSigNumerator); | |||
setIfNotNull (outTimeSig_Denominator, result.timeSigDenominator); | |||
setIfNotNull (outDeltaSampleOffsetToNextBeat, 0); //xxx | |||
setIfNotNull (outTimeSig_Numerator, (UInt32) result.timeSigNumerator); | |||
setIfNotNull (outTimeSig_Denominator, (UInt32) result.timeSigDenominator); | |||
setIfNotNull (outDeltaSampleOffsetToNextBeat, (UInt32) 0); //xxx | |||
setIfNotNull (outCurrentMeasureDownBeat, result.ppqPositionOfLastBarStart); //xxx wrong | |||
} | |||
else | |||
{ | |||
setIfNotNull (outDeltaSampleOffsetToNextBeat, 0); | |||
setIfNotNull (outTimeSig_Numerator, 4); | |||
setIfNotNull (outTimeSig_Denominator, 4); | |||
setIfNotNull (outDeltaSampleOffsetToNextBeat, (UInt32) 0); | |||
setIfNotNull (outTimeSig_Numerator, (UInt32) 4); | |||
setIfNotNull (outTimeSig_Denominator, (UInt32) 4); | |||
setIfNotNull (outCurrentMeasureDownBeat, 0); | |||
} | |||
@@ -1226,12 +1226,12 @@ private: | |||
return offsetof (AudioBufferList, mBuffers) + (sizeof (AudioBuffer) * numOutputBusChannels); | |||
} | |||
AudioBufferList* getAudioBufferListForBus (int busIndex) const noexcept | |||
AudioBufferList* getAudioBufferListForBus (AudioUnitElement busIndex) const noexcept | |||
{ | |||
return addBytesToPointer (outputBufferList.getData(), getAudioBufferSizeInBytes() * busIndex); | |||
} | |||
int getElementCount (AudioUnitScope scope) const noexcept | |||
AudioUnitElement getElementCount (AudioUnitScope scope) const noexcept | |||
{ | |||
UInt32 count; | |||
UInt32 countSize = sizeof (count); | |||
@@ -1240,7 +1240,7 @@ private: | |||
|| countSize == 0) | |||
count = 1; | |||
return (int) count; | |||
return count; | |||
} | |||
void updateNumChannels() | |||
@@ -1266,17 +1266,17 @@ private: | |||
const int outChannels = (int) supportedChannels[i].outChannels; | |||
if (inChannels < 0) | |||
maximumNumIns = jmin (maximumNumIns, inChannels); | |||
maximumNumIns = jmin (maximumNumIns, inChannels); | |||
else | |||
explicitNumIns = jmax (explicitNumIns, inChannels); | |||
if (outChannels < 0) | |||
maximumNumOuts = jmin (maximumNumOuts, outChannels); | |||
maximumNumOuts = jmin (maximumNumOuts, outChannels); | |||
else | |||
explicitNumOuts = jmax (explicitNumOuts, outChannels); | |||
} | |||
if ((maximumNumIns == -1 && maximumNumOuts == -1) // (special meaning: any number of ins/outs, as long as they match) | |||
if ((maximumNumIns == -1 && maximumNumOuts == -1) // (special meaning: any number of ins/outs, as long as they match) | |||
|| (maximumNumIns == -2 && maximumNumOuts == -1) // (special meaning: any number of ins/outs, even if they don't match) | |||
|| (maximumNumIns == -1 && maximumNumOuts == -2)) | |||
{ | |||
@@ -1284,8 +1284,8 @@ private: | |||
} | |||
else | |||
{ | |||
numInputBusChannels = explicitNumIns; | |||
numOutputBusChannels = explicitNumOuts; | |||
numInputBusChannels = (AudioUnitElement) explicitNumIns; | |||
numOutputBusChannels = (AudioUnitElement) explicitNumOuts; | |||
if (maximumNumIns == -1 || (maximumNumIns < 0 && explicitNumIns <= -maximumNumIns)) | |||
numInputBusChannels = 2; | |||
@@ -26,7 +26,7 @@ | |||
} // (juce namespace) | |||
#include <ladspa.h> | |||
#include "ladspa.h" | |||
namespace juce | |||
{ | |||
@@ -250,7 +250,7 @@ public: | |||
break; | |||
case Steinberg::Vst::Event::kDataEvent: | |||
result.addEvent (MidiMessage::createSysExMessage (e.data.bytes, e.data.size), | |||
result.addEvent (MidiMessage::createSysExMessage (e.data.bytes, (int) e.data.size), | |||
e.sampleOffset); | |||
break; | |||
@@ -300,7 +300,7 @@ public: | |||
{ | |||
e.type = Steinberg::Vst::Event::kDataEvent; | |||
e.data.bytes = msg.getSysExData(); | |||
e.data.size = msg.getSysExDataSize(); | |||
e.data.size = (uint32) msg.getSysExDataSize(); | |||
e.data.type = Steinberg::Vst::DataEvent::kMidiSysEx; | |||
} | |||
else if (msg.isAftertouch()) | |||
@@ -488,7 +488,7 @@ public: | |||
const char* const utf8 = file.getFullPathName().toRawUTF8(); | |||
if (CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, | |||
strlen (utf8), file.isDirectory())) | |||
(CFIndex) strlen (utf8), file.isDirectory())) | |||
{ | |||
bundleRef = CFBundleCreate (kCFAllocatorDefault, url); | |||
CFRelease (url); | |||
@@ -864,7 +864,7 @@ public: | |||
wantsMidiMessages = dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0; | |||
#if JUCE_MAC && JUCE_SUPPORT_CARBON | |||
usesCocoaNSView = (dispatch (effCanDo, 0, 0, (void*) "hasCockosViewAsConfig", 0) & 0xffff0000) == 0xbeef0000; | |||
usesCocoaNSView = (dispatch (effCanDo, 0, 0, (void*) "hasCockosViewAsConfig", 0) & (int) 0xffff0000) == 0xbeef0000; | |||
#endif | |||
setLatencySamples (effect->initialDelay); | |||
@@ -987,27 +987,28 @@ public: | |||
if (AudioPlayHead* const playHead = getPlayHead()) | |||
{ | |||
AudioPlayHead::CurrentPositionInfo position; | |||
playHead->getCurrentPosition (position); | |||
vstHostTime.samplePos = (double) position.timeInSamples; | |||
vstHostTime.tempo = position.bpm; | |||
vstHostTime.timeSigNumerator = position.timeSigNumerator; | |||
vstHostTime.timeSigDenominator = position.timeSigDenominator; | |||
vstHostTime.ppqPos = position.ppqPosition; | |||
vstHostTime.barStartPos = position.ppqPositionOfLastBarStart; | |||
vstHostTime.flags |= kVstTempoValid | kVstTimeSigValid | kVstPpqPosValid | kVstBarsValid; | |||
VstInt32 newTransportFlags = 0; | |||
if (position.isPlaying) newTransportFlags |= kVstTransportPlaying; | |||
if (position.isRecording) newTransportFlags |= kVstTransportRecording; | |||
if (newTransportFlags != (vstHostTime.flags & (kVstTransportPlaying | kVstTransportRecording))) | |||
vstHostTime.flags = (vstHostTime.flags & ~(kVstTransportPlaying | kVstTransportRecording)) | newTransportFlags | kVstTransportChanged; | |||
else | |||
vstHostTime.flags &= ~kVstTransportChanged; | |||
switch (position.frameRate) | |||
if (playHead->getCurrentPosition (position)) | |||
{ | |||
vstHostTime.samplePos = (double) position.timeInSamples; | |||
vstHostTime.tempo = position.bpm; | |||
vstHostTime.timeSigNumerator = position.timeSigNumerator; | |||
vstHostTime.timeSigDenominator = position.timeSigDenominator; | |||
vstHostTime.ppqPos = position.ppqPosition; | |||
vstHostTime.barStartPos = position.ppqPositionOfLastBarStart; | |||
vstHostTime.flags |= kVstTempoValid | kVstTimeSigValid | kVstPpqPosValid | kVstBarsValid; | |||
VstInt32 newTransportFlags = 0; | |||
if (position.isPlaying) newTransportFlags |= kVstTransportPlaying; | |||
if (position.isRecording) newTransportFlags |= kVstTransportRecording; | |||
if (newTransportFlags != (vstHostTime.flags & (kVstTransportPlaying | kVstTransportRecording))) | |||
vstHostTime.flags = (vstHostTime.flags & ~(kVstTransportPlaying | kVstTransportRecording)) | newTransportFlags | kVstTransportChanged; | |||
else | |||
vstHostTime.flags &= ~kVstTransportChanged; | |||
switch (position.frameRate) | |||
{ | |||
case AudioPlayHead::fps24: setHostTimeFrameRate (0, 24.0, position.timeInSeconds); break; | |||
case AudioPlayHead::fps25: setHostTimeFrameRate (1, 25.0, position.timeInSeconds); break; | |||
case AudioPlayHead::fps2997: setHostTimeFrameRate (2, 29.97, position.timeInSeconds); break; | |||
@@ -1015,17 +1016,18 @@ public: | |||
case AudioPlayHead::fps2997drop: setHostTimeFrameRate (4, 29.97, position.timeInSeconds); break; | |||
case AudioPlayHead::fps30drop: setHostTimeFrameRate (5, 29.97, position.timeInSeconds); break; | |||
default: break; | |||
} | |||
} | |||
if (position.isLooping) | |||
{ | |||
vstHostTime.cycleStartPos = position.ppqLoopStart; | |||
vstHostTime.cycleEndPos = position.ppqLoopEnd; | |||
vstHostTime.flags |= (kVstCyclePosValid | kVstTransportCycleActive); | |||
} | |||
else | |||
{ | |||
vstHostTime.flags &= ~(kVstCyclePosValid | kVstTransportCycleActive); | |||
if (position.isLooping) | |||
{ | |||
vstHostTime.cycleStartPos = position.ppqLoopStart; | |||
vstHostTime.cycleEndPos = position.ppqLoopEnd; | |||
vstHostTime.flags |= (kVstCyclePosValid | kVstTransportCycleActive); | |||
} | |||
else | |||
{ | |||
vstHostTime.flags &= ~(kVstCyclePosValid | kVstTransportCycleActive); | |||
} | |||
} | |||
} | |||
@@ -1228,8 +1230,8 @@ public: | |||
void getStateInformation (MemoryBlock& mb) override { saveToFXBFile (mb, true); } | |||
void getCurrentProgramStateInformation (MemoryBlock& mb) override { saveToFXBFile (mb, false); } | |||
void setStateInformation (const void* data, int size) override { loadFromFXBFile (data, size); } | |||
void setCurrentProgramStateInformation (const void* data, int size) override { loadFromFXBFile (data, size); } | |||
void setStateInformation (const void* data, int size) override { loadFromFXBFile (data, (size_t) size); } | |||
void setCurrentProgramStateInformation (const void* data, int size) override { loadFromFXBFile (data, (size_t) size); } | |||
//============================================================================== | |||
void timerCallback() override | |||
@@ -1282,7 +1284,12 @@ public: | |||
case audioMasterSizeWindow: | |||
if (AudioProcessorEditor* ed = getActiveEditor()) | |||
ed->setSize (index, (int) value); | |||
{ | |||
#if JUCE_LINUX | |||
const MessageManagerLock mmLock; | |||
#endif | |||
ed->setSize (index, (int) value); | |||
} | |||
return 1; | |||
@@ -1356,6 +1363,7 @@ public: | |||
"receiveVstEvents", | |||
"receiveVstMidiEvent", | |||
"supportShell", | |||
"sizeWindow", | |||
"shellCategory" }; | |||
for (int i = 0; i < numElementsInArray (canDos); ++i) | |||
@@ -1449,7 +1457,7 @@ public: | |||
{ | |||
const int oldProg = getCurrentProgram(); | |||
const int numParams = fxbSwap (((const fxProgram*) (set->programs))->numParams); | |||
const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); | |||
const int progLen = (int) sizeof (fxProgram) + (numParams - 1) * (int) sizeof (float); | |||
for (int i = 0; i < fxbSwap (set->numPrograms); ++i) | |||
{ | |||
@@ -1496,7 +1504,7 @@ public: | |||
// non-preset chunk | |||
const fxChunkSet* const cset = (const fxChunkSet*) data; | |||
if (fxbSwap (cset->chunkSize) + sizeof (fxChunkSet) - 8 > (unsigned int) dataSize) | |||
if ((size_t) fxbSwap (cset->chunkSize) + sizeof (fxChunkSet) - 8 > (size_t) dataSize) | |||
return false; | |||
setChunkData (cset->chunk, fxbSwap (cset->chunkSize), false); | |||
@@ -1506,7 +1514,7 @@ public: | |||
// preset chunk | |||
const fxProgramSet* const cset = (const fxProgramSet*) data; | |||
if (fxbSwap (cset->chunkSize) + sizeof (fxProgramSet) - 8 > (unsigned int) dataSize) | |||
if ((size_t) fxbSwap (cset->chunkSize) + sizeof (fxProgramSet) - 8 > (size_t) dataSize) | |||
return false; | |||
setChunkData (cset->chunk, fxbSwap (cset->chunkSize), true); | |||
@@ -1571,8 +1579,8 @@ public: | |||
{ | |||
if (isFXB) | |||
{ | |||
const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); | |||
const int len = (sizeof (fxSet) - sizeof (fxProgram)) + progLen * jmax (1, numPrograms); | |||
const int progLen = (int) sizeof (fxProgram) + (numParams - 1) * (int) sizeof (float); | |||
const size_t len = (sizeof (fxSet) - sizeof (fxProgram)) + (size_t) (progLen * jmax (1, numPrograms)); | |||
dest.setSize (len, true); | |||
fxSet* const set = (fxSet*) dest.getData(); | |||
@@ -1584,11 +1592,13 @@ public: | |||
set->fxVersion = fxbSwap (getVersionNumber()); | |||
set->numPrograms = fxbSwap (numPrograms); | |||
const int oldProgram = getCurrentProgram(); | |||
MemoryBlock oldSettings; | |||
createTempParameterStore (oldSettings); | |||
setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + oldProgram * progLen)); | |||
const int oldProgram = getCurrentProgram(); | |||
if (oldProgram >= 0) | |||
setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + oldProgram * progLen)); | |||
for (int i = 0; i < numPrograms; ++i) | |||
{ | |||
@@ -1599,14 +1609,14 @@ public: | |||
} | |||
} | |||
setCurrentProgram (oldProgram); | |||
if (oldProgram >= 0) | |||
setCurrentProgram (oldProgram); | |||
restoreFromTempParameterStore (oldSettings); | |||
} | |||
else | |||
{ | |||
const int totalLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); | |||
dest.setSize (totalLen, true); | |||
dest.setSize (sizeof (fxProgram) + (size_t) ((numParams - 1) * (int) sizeof (float)), true); | |||
setParamsInProgramBlock ((fxProgram*) dest.getData()); | |||
} | |||
} | |||
@@ -1621,7 +1631,7 @@ public: | |||
if (usesChunks()) | |||
{ | |||
void* data = nullptr; | |||
const VstIntPtr bytes = dispatch (effGetChunk, isPreset ? 1 : 0, 0, &data, 0.0f); | |||
const size_t bytes = (size_t) dispatch (effGetChunk, isPreset ? 1 : 0, 0, &data, 0.0f); | |||
if (data != nullptr && bytes <= maxSizeMB * 1024 * 1024) | |||
{ | |||
@@ -1783,7 +1793,7 @@ private: | |||
//============================================================================== | |||
void createTempParameterStore (MemoryBlock& dest) | |||
{ | |||
dest.setSize (64 + 4 * getNumParameters()); | |||
dest.setSize (64 + 4 * (size_t) getNumParameters()); | |||
dest.fillWith (0); | |||
getCurrentProgramName().copyToUTF8 ((char*) dest.getData(), 63); | |||
@@ -1821,21 +1831,21 @@ private: | |||
String s; | |||
if (v == 0 || (int) v == -1) | |||
v = getVersionNumber(); | |||
v = (unsigned int) getVersionNumber(); | |||
if (v != 0) | |||
{ | |||
int versionBits[32]; | |||
int n = 0; | |||
for (int vv = v; vv != 0; vv /= 10) | |||
for (unsigned int vv = v; vv != 0; vv /= 10) | |||
versionBits [n++] = vv % 10; | |||
if (n > 4) // if the number ends up silly, it's probably encoded as hex instead of decimal.. | |||
{ | |||
n = 0; | |||
for (int vv = v; vv != 0; vv >>= 8) | |||
for (unsigned int vv = v; vv != 0; vv >>= 8) | |||
versionBits [n++] = vv & 255; | |||
} | |||
@@ -1968,9 +1978,13 @@ public: | |||
#elif JUCE_LINUX | |||
if (pluginWindow != 0) | |||
{ | |||
XResizeWindow (display, pluginWindow, getWidth(), getHeight()); | |||
XMoveWindow (display, pluginWindow, pos.getX(), pos.getY()); | |||
XMoveResizeWindow (display, pluginWindow, | |||
pos.getX(), pos.getY(), | |||
(unsigned int) getWidth(), | |||
(unsigned int) getHeight()); | |||
XMapRaised (display, pluginWindow); | |||
XFlush (display); | |||
} | |||
#endif | |||
@@ -2081,6 +2095,16 @@ public: | |||
plugin.dispatch (effEditIdle, 0, 0, 0, 0); | |||
reentrant = false; | |||
} | |||
#if JUCE_LINUX | |||
if (pluginWindow == 0) | |||
{ | |||
updatePluginWindowHandle(); | |||
if (pluginWindow != 0) | |||
componentMovedOrResized (true, true); | |||
} | |||
#endif | |||
} | |||
} | |||
@@ -2266,11 +2290,7 @@ private: | |||
} | |||
#elif JUCE_LINUX | |||
pluginWindow = getChildWindow ((Window) getWindowHandle()); | |||
if (pluginWindow != 0) | |||
pluginProc = (EventProcPtr) getPropertyFromXWindow (pluginWindow, | |||
XInternAtom (display, "_XEventProc", False)); | |||
updatePluginWindowHandle(); | |||
int w = 250, h = 150; | |||
@@ -2498,6 +2518,15 @@ private: | |||
sendEventToChild (ev); | |||
} | |||
} | |||
void updatePluginWindowHandle() | |||
{ | |||
pluginWindow = getChildWindow ((Window) getWindowHandle()); | |||
if (pluginWindow != 0) | |||
pluginProc = (EventProcPtr) getPropertyFromXWindow (pluginWindow, | |||
XInternAtom (display, "_XEventProc", False)); | |||
} | |||
#endif | |||
//============================================================================== | |||
@@ -128,7 +128,9 @@ public: | |||
//============================================================================== | |||
/** Fills-in the given structure with details about the transport's | |||
position at the start of the current processing block. | |||
position at the start of the current processing block. If this method returns | |||
false then the current play head position is not available and the given | |||
structure will be undefined. | |||
You can ONLY call this from your processBlock() method! Calling it at other | |||
times will produce undefined behaviour, as the host may not have any context | |||
@@ -50,7 +50,7 @@ AudioProcessor::~AudioProcessor() | |||
jassert (activeEditor == nullptr); | |||
#endif | |||
#if JUCE_DEBUG | |||
#if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING | |||
// This will fail if you've called beginParameterChangeGesture() for one | |||
// or more parameters without having made a corresponding call to endParameterChangeGesture... | |||
jassert (changingParams.countNumberOfSetBits() == 0); | |||
@@ -144,7 +144,7 @@ void AudioProcessor::beginParameterChangeGesture (int parameterIndex) | |||
{ | |||
if (isPositiveAndBelow (parameterIndex, getNumParameters())) | |||
{ | |||
#if JUCE_DEBUG | |||
#if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING | |||
// This means you've called beginParameterChangeGesture twice in succession without a matching | |||
// call to endParameterChangeGesture. That might be fine in most hosts, but better to avoid doing it. | |||
jassert (! changingParams [parameterIndex]); | |||
@@ -165,9 +165,9 @@ void AudioProcessor::endParameterChangeGesture (int parameterIndex) | |||
{ | |||
if (isPositiveAndBelow (parameterIndex, getNumParameters())) | |||
{ | |||
#if JUCE_DEBUG | |||
#if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING | |||
// This means you've called endParameterChangeGesture without having previously called | |||
// endParameterChangeGesture. That might be fine in most hosts, but better to keep the | |||
// beginParameterChangeGesture. That might be fine in most hosts, but better to keep the | |||
// calls matched correctly. | |||
jassert (changingParams [parameterIndex]); | |||
changingParams.clearBit (parameterIndex); | |||
@@ -384,10 +384,17 @@ public: | |||
//============================================================================== | |||
/** This must return the correct value immediately after the object has been | |||
created, and mustn't change the number of parameters later. | |||
NOTE! This method will eventually be deprecated! It's recommended that you use the | |||
AudioProcessorParameter class instead to manage your parameters. | |||
*/ | |||
virtual int getNumParameters(); | |||
/** Returns the name of a particular parameter. */ | |||
/** Returns the name of a particular parameter. | |||
NOTE! This method will eventually be deprecated! It's recommended that you use the | |||
AudioProcessorParameter class instead to manage your parameters. | |||
*/ | |||
virtual const String getParameterName (int parameterIndex); | |||
/** Called by the host to find out the value of one of the filter's parameters. | |||
@@ -397,27 +404,39 @@ public: | |||
This could be called quite frequently, so try to make your code efficient. | |||
It's also likely to be called by non-UI threads, so the code in here should | |||
be thread-aware. | |||
NOTE! This method will eventually be deprecated! It's recommended that you use the | |||
AudioProcessorParameter class instead to manage your parameters. | |||
*/ | |||
virtual float getParameter (int parameterIndex); | |||
/** Returns the value of a parameter as a text string. */ | |||
virtual const String getParameterText (int parameterIndex); | |||
/** Returns the name of a parameter as a text string with a preferred maximum length. | |||
If you want to provide customised short versions of your parameter names that | |||
will look better in constrained spaces (e.g. the displays on hardware controller | |||
devices or mixing desks) then you should implement this method. | |||
If you don't override it, the default implementation will call getParameterText(int), | |||
and truncate the result. | |||
NOTE! This method will eventually be deprecated! It's recommended that you use | |||
AudioProcessorParameter::getName() instead. | |||
*/ | |||
virtual String getParameterName (int parameterIndex, int maximumStringLength); | |||
/** Returns the value of a parameter as a text string. | |||
NOTE! This method will eventually be deprecated! It's recommended that you use | |||
AudioProcessorParameter::getText() instead. | |||
*/ | |||
virtual const String getParameterText (int parameterIndex); | |||
/** Returns the value of a parameter as a text string with a preferred maximum length. | |||
If you want to provide customised short versions of your parameter values that | |||
will look better in constrained spaces (e.g. the displays on hardware controller | |||
devices or mixing desks) then you should implement this method. | |||
If you don't override it, the default implementation will call getParameterText(int), | |||
and truncate the result. | |||
NOTE! This method will eventually be deprecated! It's recommended that you use | |||
AudioProcessorParameter::getText() instead. | |||
*/ | |||
virtual String getParameterText (int parameterIndex, int maximumStringLength); | |||
@@ -426,10 +445,16 @@ public: | |||
AudioProcessor::getDefaultNumParameterSteps(). | |||
If your parameter is boolean, then you may want to make this return 2. | |||
The value that is returned may or may not be used, depending on the host. | |||
NOTE! This method will eventually be deprecated! It's recommended that you use | |||
AudioProcessorParameter::getNumSteps() instead. | |||
*/ | |||
virtual int getParameterNumSteps (int parameterIndex); | |||
/** Returns the default number of steps for a parameter. | |||
NOTE! This method will eventually be deprecated! It's recommended that you use | |||
AudioProcessorParameter::getNumSteps() instead. | |||
@see getParameterNumSteps | |||
*/ | |||
static int getDefaultNumParameterSteps() noexcept; | |||
@@ -437,16 +462,25 @@ public: | |||
/** Returns the default value for the parameter. | |||
By default, this just returns 0. | |||
The value that is returned may or may not be used, depending on the host. | |||
NOTE! This method will eventually be deprecated! It's recommended that you use | |||
AudioProcessorParameter::getDefaultValue() instead. | |||
*/ | |||
virtual float getParameterDefaultValue (int parameterIndex); | |||
/** Some plugin types may be able to return a label string for a | |||
parameter's units. | |||
NOTE! This method will eventually be deprecated! It's recommended that you use | |||
AudioProcessorParameter::getLabel() instead. | |||
*/ | |||
virtual String getParameterLabel (int index) const; | |||
/** This can be overridden to tell the host that particular parameters operate in the | |||
reverse direction. (Not all plugin formats or hosts will actually use this information). | |||
NOTE! This method will eventually be deprecated! It's recommended that you use | |||
AudioProcessorParameter::isOrientationInverted() instead. | |||
*/ | |||
virtual bool isParameterOrientationInverted (int index) const; | |||
@@ -462,6 +496,9 @@ public: | |||
won't be able to automate your parameters properly. | |||
The value passed will be between 0 and 1.0. | |||
NOTE! This method will eventually be deprecated! It's recommended that you use | |||
AudioProcessorParameter::setValue() instead. | |||
*/ | |||
virtual void setParameter (int parameterIndex, float newValue); | |||
@@ -474,11 +511,17 @@ public: | |||
Note that to make sure the host correctly handles automation, you should call | |||
the beginParameterChangeGesture() and endParameterChangeGesture() methods to | |||
tell the host when the user has started and stopped changing the parameter. | |||
NOTE! This method will eventually be deprecated! It's recommended that you use | |||
AudioProcessorParameter::setValueNotifyingHost() instead. | |||
*/ | |||
void setParameterNotifyingHost (int parameterIndex, float newValue); | |||
/** Returns true if the host can automate this parameter. | |||
By default, this returns true for all parameters. | |||
NOTE! This method will eventually be deprecated! It's recommended that you use | |||
AudioProcessorParameter::isAutomatable() instead. | |||
*/ | |||
virtual bool isParameterAutomatable (int parameterIndex) const; | |||
@@ -486,6 +529,9 @@ public: | |||
A meta-parameter is a parameter that changes other params. It is used | |||
by some hosts (e.g. AudioUnit hosts). | |||
By default this returns false. | |||
NOTE! This method will eventually be deprecated! It's recommended that you use | |||
AudioProcessorParameter::isMetaParameter() instead. | |||
*/ | |||
virtual bool isMetaParameter (int parameterIndex) const; | |||
@@ -496,6 +542,9 @@ public: | |||
it may use this information to help it record automation. | |||
If you call this, it must be matched by a later call to endParameterChangeGesture(). | |||
NOTE! This method will eventually be deprecated! It's recommended that you use | |||
AudioProcessorParameter::beginChangeGesture() instead. | |||
*/ | |||
void beginParameterChangeGesture (int parameterIndex); | |||
@@ -505,6 +554,9 @@ public: | |||
it may use this information to help it record automation. | |||
A call to this method must follow a call to beginParameterChangeGesture(). | |||
NOTE! This method will eventually be deprecated! It's recommended that you use | |||
AudioProcessorParameter::endChangeGesture() instead. | |||
*/ | |||
void endParameterChangeGesture (int parameterIndex); | |||
@@ -693,7 +745,7 @@ private: | |||
OwnedArray<AudioProcessorParameter> managedParameters; | |||
AudioProcessorParameter* getParamChecked (int) const noexcept; | |||
#if JUCE_DEBUG | |||
#if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING | |||
BigInteger changingParams; | |||
#endif | |||
@@ -308,7 +308,7 @@ public: | |||
void fillInPluginDescription (PluginDescription&) const override; | |||
void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock) override; | |||
void releaseResources() override; | |||
void processBlock (AudioSampleBuffer&, MidiBuffer&); | |||
void processBlock (AudioSampleBuffer&, MidiBuffer&) override; | |||
const String getInputChannelName (int channelIndex) const override; | |||
const String getOutputChannelName (int channelIndex) const override; | |||
@@ -68,7 +68,7 @@ public: | |||
/** Your filter can call this when it needs to change one of its parameters. | |||
This could happen when the editor or some other internal operation changes | |||
a parameter. This method will call the setParameter() method to change the | |||
a parameter. This method will call the setValue() method to change the | |||
value, and will then send a message to the host telling it about the change. | |||
Note that to make sure the host correctly handles automation, you should call | |||
@@ -85,7 +85,7 @@ public: | |||
void deleteKeyPressed (int) override | |||
{ | |||
owner.removeSelected(); | |||
owner.removeSelectedPlugins(); | |||
} | |||
void sortOrderChanged (int newSortColumnId, bool isForwards) override | |||
@@ -102,14 +102,6 @@ public: | |||
} | |||
} | |||
static void removePluginItem (KnownPluginList& list, int index) | |||
{ | |||
if (index < list.getNumTypes()) | |||
list.removeType (index); | |||
else | |||
list.removeFromBlacklist (list.getBlacklistedFiles() [index - list.getNumTypes()]); | |||
} | |||
static String getPluginDescription (const PluginDescription& desc) | |||
{ | |||
StringArray items; | |||
@@ -179,6 +171,12 @@ void PluginListComponent::setOptionsButtonText (const String& newText) | |||
resized(); | |||
} | |||
void PluginListComponent::setScanDialogText (const String& title, const String& content) | |||
{ | |||
dialogTitle = title; | |||
dialogText = content; | |||
} | |||
void PluginListComponent::setNumberOfThreadsForScanning (int num) | |||
{ | |||
numThreads = num; | |||
@@ -207,13 +205,24 @@ void PluginListComponent::updateList() | |||
table.repaint(); | |||
} | |||
void PluginListComponent::removeSelected() | |||
void PluginListComponent::removeSelectedPlugins() | |||
{ | |||
const SparseSet<int> selected (table.getSelectedRows()); | |||
for (int i = table.getNumRows(); --i >= 0;) | |||
if (selected.contains (i)) | |||
TableModel::removePluginItem (list, i); | |||
removePluginItem (i); | |||
} | |||
void PluginListComponent::setTableModel (TableListBoxModel* model) | |||
{ | |||
table.setModel (nullptr); | |||
tableModel = model; | |||
table.setModel (tableModel); | |||
table.getHeader().reSortTable(); | |||
table.updateContent(); | |||
table.repaint(); | |||
} | |||
bool PluginListComponent::canShowSelectedFolder() const | |||
@@ -238,6 +247,14 @@ void PluginListComponent::removeMissingPlugins() | |||
list.removeType (i); | |||
} | |||
void PluginListComponent::removePluginItem (int index) | |||
{ | |||
if (index < list.getNumTypes()) | |||
list.removeType (index); | |||
else | |||
list.removeFromBlacklist (list.getBlacklistedFiles() [index - list.getNumTypes()]); | |||
} | |||
void PluginListComponent::optionsMenuStaticCallback (int result, PluginListComponent* pluginList) | |||
{ | |||
if (pluginList != nullptr) | |||
@@ -250,7 +267,7 @@ void PluginListComponent::optionsMenuCallback (int result) | |||
{ | |||
case 0: break; | |||
case 1: list.clear(); break; | |||
case 2: removeSelected(); break; | |||
case 2: removeSelectedPlugins(); break; | |||
case 3: showSelectedFolder(); break; | |||
case 4: removeMissingPlugins(); break; | |||
@@ -293,7 +310,7 @@ bool PluginListComponent::isInterestedInFileDrag (const StringArray& /*files*/) | |||
void PluginListComponent::filesDropped (const StringArray& files, int, int) | |||
{ | |||
OwnedArray <PluginDescription> typesFound; | |||
OwnedArray<PluginDescription> typesFound; | |||
list.scanAndAddDragAndDroppedFiles (formatManager, files, typesFound); | |||
} | |||
@@ -313,11 +330,11 @@ void PluginListComponent::setLastSearchPath (PropertiesFile& properties, AudioPl | |||
class PluginListComponent::Scanner : private Timer | |||
{ | |||
public: | |||
Scanner (PluginListComponent& plc, AudioPluginFormat& format, PropertiesFile* properties, int threads) | |||
Scanner (PluginListComponent& plc, AudioPluginFormat& format, PropertiesFile* properties, | |||
int threads, const String& title, const String& text) | |||
: owner (plc), formatToScan (format), propertiesToUse (properties), | |||
pathChooserWindow (TRANS("Select folders to scan..."), String::empty, AlertWindow::NoIcon), | |||
progressWindow (TRANS("Scanning for plug-ins..."), | |||
TRANS("Searching for all possible plug-in files..."), AlertWindow::NoIcon), | |||
progressWindow (title, text, AlertWindow::NoIcon), | |||
progress (0.0), numThreads (threads), finished (false) | |||
{ | |||
FileSearchPath path (formatToScan.getDefaultLocationsToSearch()); | |||
@@ -528,7 +545,9 @@ private: | |||
void PluginListComponent::scanFor (AudioPluginFormat& format) | |||
{ | |||
currentScanner = new Scanner (*this, format, propertiesToUse, numThreads); | |||
currentScanner = new Scanner (*this, format, propertiesToUse, numThreads, | |||
dialogTitle.isNotEmpty() ? dialogTitle : TRANS("Scanning for plug-ins..."), | |||
dialogText.isNotEmpty() ? dialogText : TRANS("Searching for all possible plug-in files...")); | |||
} | |||
bool PluginListComponent::isScanning() const noexcept | |||
@@ -55,6 +55,10 @@ public: | |||
/** Changes the text in the panel's options button. */ | |||
void setOptionsButtonText (const String& newText); | |||
/** Changes the text in the progress dialog box that is shown when scanning. */ | |||
void setScanDialogText (const String& textForProgressWindowTitle, | |||
const String& textForProgressWindowDescription); | |||
/** Sets how many threads to simultaneously scan for plugins. | |||
If this is 0, then all scanning happens on the message thread (this is the default) | |||
*/ | |||
@@ -72,6 +76,17 @@ public: | |||
/** Returns true if there's currently a scan in progress. */ | |||
bool isScanning() const noexcept; | |||
/** Removes the plugins currently selected in the table. */ | |||
void removeSelectedPlugins(); | |||
/** Sets a custom table model to be used. | |||
This will take ownership of the model and delete it when no longer needed. | |||
*/ | |||
void setTableModel (TableListBoxModel* model); | |||
/** Returns the table used to display the plugin list. */ | |||
TableListBox& getTableListBox() noexcept { return table; } | |||
private: | |||
//============================================================================== | |||
AudioPluginFormatManager& formatManager; | |||
@@ -80,12 +95,11 @@ private: | |||
TableListBox table; | |||
TextButton optionsButton; | |||
PropertiesFile* propertiesToUse; | |||
String dialogTitle, dialogText; | |||
int numThreads; | |||
class TableModel; | |||
friend class TableModel; | |||
friend struct ContainerDeletePolicy<TableModel>; | |||
ScopedPointer<TableModel> tableModel; | |||
ScopedPointer<TableListBoxModel> tableModel; | |||
class Scanner; | |||
friend class Scanner; | |||
@@ -98,8 +112,8 @@ private: | |||
void updateList(); | |||
void showSelectedFolder(); | |||
bool canShowSelectedFolder() const; | |||
void removeSelected(); | |||
void removeMissingPlugins(); | |||
void removePluginItem (int index); | |||
void resized() override; | |||
bool isInterestedInFileDrag (const StringArray&) override; | |||
@@ -69,7 +69,7 @@ | |||
void readFromFifo (int* someData, int numItems) | |||
{ | |||
int start1, size1, start2, size2; | |||
abstractFifo.prepareToRead (numSamples, start1, size1, start2, size2); | |||
abstractFifo.prepareToRead (numItems, start1, size1, start2, size2); | |||
if (size1 > 0) | |||
copySomeData (someData, myBuffer + start1, size1); | |||
@@ -832,8 +832,8 @@ public: | |||
/** Removes an item from the array. | |||
This will remove the first occurrence of the given element from the array. | |||
If the item isn't found, no action is taken. | |||
This will remove all occurrences of the given element from the array. | |||
If no such items are found, no action is taken. | |||
@param valueToRemove the object to try to remove | |||
@see remove, removeRange | |||
@@ -119,26 +119,30 @@ public: | |||
*/ | |||
~ReferenceCountedArray() | |||
{ | |||
clear(); | |||
releaseAllObjects(); | |||
} | |||
//============================================================================== | |||
/** Removes all objects from the array. | |||
Any objects in the array that are not referenced from elsewhere will be deleted. | |||
Any objects in the array that whose reference counts drop to zero will be deleted. | |||
*/ | |||
void clear() | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
while (numUsed > 0) | |||
if (ObjectClass* o = data.elements [--numUsed]) | |||
releaseObject (o); | |||
jassert (numUsed == 0); | |||
releaseAllObjects(); | |||
data.setAllocatedSize (0); | |||
} | |||
/** Removes all objects from the array without freeing the array's allocated storage. | |||
Any objects in the array that whose reference counts drop to zero will be deleted. | |||
@see clear | |||
*/ | |||
void clearQuick() | |||
{ | |||
const ScopedLockType lock (getLock()); | |||
releaseAllObjects(); | |||
} | |||
/** Returns the current number of objects in the array. */ | |||
inline int size() const noexcept | |||
{ | |||
@@ -886,6 +890,15 @@ private: | |||
ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse> data; | |||
int numUsed; | |||
void releaseAllObjects() | |||
{ | |||
while (numUsed > 0) | |||
if (ObjectClass* o = data.elements [--numUsed]) | |||
releaseObject (o); | |||
jassert (numUsed == 0); | |||
} | |||
static void releaseObject (ObjectClass* o) | |||
{ | |||
if (o->decReferenceCountWithoutDeleting()) | |||
@@ -57,7 +57,7 @@ int FileInputStream::read (void* buffer, int bytesToRead) | |||
jassert (buffer != nullptr && bytesToRead >= 0); | |||
const size_t num = readInternal (buffer, (size_t) bytesToRead); | |||
currentPosition += num; | |||
currentPosition += (int64) num; | |||
return (int) num; | |||
} | |||
@@ -90,7 +90,7 @@ bool FileOutputStream::write (const void* const src, const size_t numBytes) | |||
{ | |||
memcpy (buffer + bytesInBuffer, src, numBytes); | |||
bytesInBuffer += numBytes; | |||
currentPosition += numBytes; | |||
currentPosition += (int64) numBytes; | |||
} | |||
else | |||
{ | |||
@@ -101,7 +101,7 @@ bool FileOutputStream::write (const void* const src, const size_t numBytes) | |||
{ | |||
memcpy (buffer + bytesInBuffer, src, numBytes); | |||
bytesInBuffer += numBytes; | |||
currentPosition += numBytes; | |||
currentPosition += (int64) numBytes; | |||
} | |||
else | |||
{ | |||
@@ -110,7 +110,7 @@ bool FileOutputStream::write (const void* const src, const size_t numBytes) | |||
if (bytesWritten < 0) | |||
return false; | |||
currentPosition += bytesWritten; | |||
currentPosition += (int64) bytesWritten; | |||
return bytesWritten == (ssize_t) numBytes; | |||
} | |||
} | |||
@@ -126,7 +126,7 @@ bool FileOutputStream::writeRepeatedByte (uint8 byte, size_t numBytes) | |||
{ | |||
memset (buffer + bytesInBuffer, byte, numBytes); | |||
bytesInBuffer += numBytes; | |||
currentPosition += numBytes; | |||
currentPosition += (int64) numBytes; | |||
return true; | |||
} | |||
@@ -579,7 +579,7 @@ public: | |||
case 1: return r.nextInt(); | |||
case 2: return r.nextInt64(); | |||
case 3: return r.nextBool(); | |||
case 4: return r.nextDouble(); | |||
case 4: return String (r.nextDouble(), 8).getDoubleValue(); | |||
case 5: return createRandomWideCharString (r); | |||
case 6: | |||
@@ -149,6 +149,10 @@ struct JavascriptEngine::RootObject : public DynamicObject | |||
if (const var* prop = getPropertyPointer (p, functionName)) | |||
return *prop; | |||
} | |||
// if there's a class with an overridden DynamicObject::hasMethod, this avoids an error | |||
if (o->hasMethod (functionName)) | |||
return var(); | |||
} | |||
if (targetObject.isString()) | |||
@@ -699,6 +703,11 @@ struct JavascriptEngine::RootObject : public DynamicObject | |||
if (FunctionObject* fo = dynamic_cast<FunctionObject*> (function.getObject())) | |||
return fo->invoke (s, args); | |||
if (DotOperator* dot = dynamic_cast<DotOperator*> (object.get())) | |||
if (DynamicObject* o = thisObject.getDynamicObject()) | |||
if (o->hasMethod (dot->child)) // allow an overridden DynamicObject::invokeMethod to accept a method call. | |||
return o->invokeMethod (dot->child, args); | |||
location.throwError ("This expression is not a function!"); return var(); | |||
} | |||
@@ -107,6 +107,11 @@ | |||
#include <android/log.h> | |||
#endif | |||
//============================================================================== | |||
#ifndef JUCE_STANDALONE_APPLICATION | |||
JUCE_COMPILER_WARNING ("Please re-save your Introjucer project with the latest Introjucer version to avoid this warning") | |||
#define JUCE_STANDALONE_APPLICATION 0 | |||
#endif | |||
//============================================================================== | |||
namespace juce | |||
@@ -274,6 +274,19 @@ inline void swapVariables (Type& variable1, Type& variable2) | |||
std::swap (variable1, variable2); | |||
} | |||
/** Handy function for avoiding unused variables warning. */ | |||
template <typename Type1> | |||
void ignoreUnused (const Type1&) noexcept {} | |||
template <typename Type1, typename Type2> | |||
void ignoreUnused (const Type1&, const Type2&) noexcept {} | |||
template <typename Type1, typename Type2, typename Type3> | |||
void ignoreUnused (const Type1&, const Type2&, const Type3&) noexcept {} | |||
template <typename Type1, typename Type2, typename Type3, typename Type4> | |||
void ignoreUnused (const Type1&, const Type2&, const Type3&, const Type4&) noexcept {} | |||
/** Handy function for getting the number of elements in a simple const C array. | |||
E.g. | |||
@code | |||
@@ -299,11 +312,23 @@ template <typename Type> | |||
inline Type juce_hypot (Type a, Type b) noexcept | |||
{ | |||
#if JUCE_MSVC | |||
return static_cast <Type> (_hypot (a, b)); | |||
return static_cast<Type> (_hypot (a, b)); | |||
#else | |||
return static_cast<Type> (hypot (a, b)); | |||
#endif | |||
} | |||
#ifndef DOXYGEN | |||
template <> | |||
inline float juce_hypot (float a, float b) noexcept | |||
{ | |||
#if JUCE_MSVC | |||
return (_hypotf (a, b)); | |||
#else | |||
return static_cast <Type> (hypot (a, b)); | |||
return (hypotf (a, b)); | |||
#endif | |||
} | |||
#endif | |||
/** 64-bit abs function. */ | |||
inline int64 abs64 (const int64 n) noexcept | |||
@@ -333,13 +358,27 @@ const float float_Pi = 3.14159265358979323846f; | |||
/** The isfinite() method seems to vary between platforms, so this is a | |||
platform-independent function for it. | |||
*/ | |||
template <typename FloatingPointType> | |||
inline bool juce_isfinite (FloatingPointType value) | |||
template <typename NumericType> | |||
inline bool juce_isfinite (NumericType) noexcept | |||
{ | |||
#if JUCE_WINDOWS | |||
return _finite (value); | |||
#elif JUCE_ANDROID | |||
return isfinite (value); | |||
return true; // Integer types are always finite | |||
} | |||
template <> | |||
inline bool juce_isfinite (float value) noexcept | |||
{ | |||
#if JUCE_MSVC | |||
return _finite (value) != 0; | |||
#else | |||
return std::isfinite (value); | |||
#endif | |||
} | |||
template <> | |||
inline bool juce_isfinite (double value) noexcept | |||
{ | |||
#if JUCE_MSVC | |||
return _finite (value) != 0; | |||
#else | |||
return std::isfinite (value); | |||
#endif | |||
@@ -495,12 +534,12 @@ NumericType square (NumericType n) noexcept | |||
} | |||
//============================================================================== | |||
#if (JUCE_INTEL && JUCE_32BIT) || defined (DOXYGEN) | |||
#if JUCE_INTEL || defined (DOXYGEN) | |||
/** This macro can be applied to a float variable to check whether it contains a denormalised | |||
value, and to normalise it if necessary. | |||
On CPUs that aren't vulnerable to denormalisation problems, this will have no effect. | |||
*/ | |||
#define JUCE_UNDENORMALISE(x) x += 1.0f; x -= 1.0f; | |||
#define JUCE_UNDENORMALISE(x) { (x) += 0.1f; (x) -= 0.1f; } | |||
#else | |||
#define JUCE_UNDENORMALISE(x) | |||
#endif | |||
@@ -153,7 +153,7 @@ inline uint64 ByteOrder::swap (uint64 value) noexcept | |||
#elif JUCE_USE_MSVC_INTRINSICS | |||
return _byteswap_uint64 (value); | |||
#else | |||
return (((int64) swap ((uint32) value)) << 32) | swap ((uint32) (value >> 32)); | |||
return (((uint64) swap ((uint32) value)) << 32) | swap ((uint32) (value >> 32)); | |||
#endif | |||
} | |||
@@ -36,7 +36,7 @@ namespace HeapBlockHelper | |||
struct ThrowOnFail { static void check (void*) {} }; | |||
template<> | |||
struct ThrowOnFail <true> { static void check (void* data) { if (data == nullptr) throw std::bad_alloc(); } }; | |||
struct ThrowOnFail<true> { static void check (void* data) { if (data == nullptr) throw std::bad_alloc(); } }; | |||
} | |||
#endif | |||
@@ -67,7 +67,7 @@ namespace HeapBlockHelper | |||
..you could just write this: | |||
@code | |||
HeapBlock <int> temp (1024); | |||
HeapBlock<int> temp (1024); | |||
memcpy (temp, xyz, 1024 * sizeof (int)); | |||
temp.calloc (2048); | |||
temp[0] = 1234; | |||
@@ -109,7 +109,7 @@ public: | |||
other constructor that takes an InitialisationState parameter. | |||
*/ | |||
explicit HeapBlock (const size_t numElements) | |||
: data (static_cast <ElementType*> (std::malloc (numElements * sizeof (ElementType)))) | |||
: data (static_cast<ElementType*> (std::malloc (numElements * sizeof (ElementType)))) | |||
{ | |||
throwOnAllocationFailure(); | |||
} | |||
@@ -120,7 +120,7 @@ public: | |||
or left uninitialised. | |||
*/ | |||
HeapBlock (const size_t numElements, const bool initialiseToZero) | |||
: data (static_cast <ElementType*> (initialiseToZero | |||
: data (static_cast<ElementType*> (initialiseToZero | |||
? std::calloc (numElements, sizeof (ElementType)) | |||
: std::malloc (numElements * sizeof (ElementType)))) | |||
{ | |||
@@ -166,13 +166,13 @@ public: | |||
This may be a null pointer if the data hasn't yet been allocated, or if it has been | |||
freed by calling the free() method. | |||
*/ | |||
inline operator void*() const noexcept { return static_cast <void*> (data); } | |||
inline operator void*() const noexcept { return static_cast<void*> (data); } | |||
/** Returns a void pointer to the allocated data. | |||
This may be a null pointer if the data hasn't yet been allocated, or if it has been | |||
freed by calling the free() method. | |||
*/ | |||
inline operator const void*() const noexcept { return static_cast <const void*> (data); } | |||
inline operator const void*() const noexcept { return static_cast<const void*> (data); } | |||
/** Lets you use indirect calls to the first element in the array. | |||
Obviously this will cause problems if the array hasn't been initialised, because it'll | |||
@@ -220,7 +220,7 @@ public: | |||
void malloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType)) | |||
{ | |||
std::free (data); | |||
data = static_cast <ElementType*> (std::malloc (newNumElements * elementSize)); | |||
data = static_cast<ElementType*> (std::malloc (newNumElements * elementSize)); | |||
throwOnAllocationFailure(); | |||
} | |||
@@ -230,7 +230,7 @@ public: | |||
void calloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType)) | |||
{ | |||
std::free (data); | |||
data = static_cast <ElementType*> (std::calloc (newNumElements, elementSize)); | |||
data = static_cast<ElementType*> (std::calloc (newNumElements, elementSize)); | |||
throwOnAllocationFailure(); | |||
} | |||
@@ -241,7 +241,7 @@ public: | |||
void allocate (const size_t newNumElements, bool initialiseToZero) | |||
{ | |||
std::free (data); | |||
data = static_cast <ElementType*> (initialiseToZero | |||
data = static_cast<ElementType*> (initialiseToZero | |||
? std::calloc (newNumElements, sizeof (ElementType)) | |||
: std::malloc (newNumElements * sizeof (ElementType))); | |||
throwOnAllocationFailure(); | |||
@@ -254,15 +254,15 @@ public: | |||
*/ | |||
void realloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType)) | |||
{ | |||
data = static_cast <ElementType*> (data == nullptr ? std::malloc (newNumElements * elementSize) | |||
: std::realloc (data, newNumElements * elementSize)); | |||
data = static_cast<ElementType*> (data == nullptr ? std::malloc (newNumElements * elementSize) | |||
: std::realloc (data, newNumElements * elementSize)); | |||
throwOnAllocationFailure(); | |||
} | |||
/** Frees any currently-allocated data. | |||
This will free the data and reset this object to be a null pointer. | |||
*/ | |||
void free() | |||
void free() noexcept | |||
{ | |||
std::free (data); | |||
data = nullptr; | |||
@@ -272,7 +272,7 @@ public: | |||
The two objects simply exchange their data pointers. | |||
*/ | |||
template <bool otherBlockThrows> | |||
void swapWith (HeapBlock <ElementType, otherBlockThrows>& other) noexcept | |||
void swapWith (HeapBlock<ElementType, otherBlockThrows>& other) noexcept | |||
{ | |||
std::swap (data, other.data); | |||
} | |||
@@ -259,7 +259,7 @@ void MemoryBlock::copyTo (void* const dst, int offset, size_t num) const noexcep | |||
if ((size_t) offset + num > size) | |||
{ | |||
const size_t newNum = size - (size_t) offset; | |||
const size_t newNum = (size_t) size - (size_t) offset; | |||
zeromem (d + newNum, num - newNum); | |||
num = newNum; | |||
} | |||
@@ -44,7 +44,7 @@ | |||
destructor, in case it is deleted by other means than deleteInstance() | |||
Clients can then call the static method MyClass::getInstance() to get a pointer | |||
to the singleton, or MyClass::getInstanceWithoutCreating() which will return 0 if | |||
to the singleton, or MyClass::getInstanceWithoutCreating() which will return nullptr if | |||
no instance currently exists. | |||
e.g. @code | |||
@@ -331,15 +331,26 @@ public class JuceAppActivity extends Activity | |||
setFocusableInTouchMode (true); | |||
setOnFocusChangeListener (this); | |||
requestFocus(); | |||
// swap red and blue colours to match internal opengl texture format | |||
ColorMatrix colorMatrix = new ColorMatrix(); | |||
float[] colorTransform = { 0, 0, 1.0f, 0, 0, | |||
0, 1.0f, 0, 0, 0, | |||
1.0f, 0, 0, 0, 0, | |||
0, 0, 0, 1.0f, 0 }; | |||
colorMatrix.set (colorTransform); | |||
paint.setColorFilter (new ColorMatrixColorFilter (colorMatrix)); | |||
} | |||
//============================================================================== | |||
private native void handlePaint (long host, Canvas canvas); | |||
private native void handlePaint (long host, Canvas canvas, Paint paint); | |||
@Override | |||
public void onDraw (Canvas canvas) | |||
{ | |||
handlePaint (host, canvas); | |||
handlePaint (host, canvas, paint); | |||
} | |||
@Override | |||
@@ -350,6 +361,7 @@ public class JuceAppActivity extends Activity | |||
private boolean opaque; | |||
private long host; | |||
private Paint paint = new Paint(); | |||
//============================================================================== | |||
private native void handleMouseDown (long host, int index, float x, float y, long time); | |||
@@ -448,6 +460,22 @@ public class JuceAppActivity extends Activity | |||
return true; | |||
} | |||
@Override | |||
public boolean onKeyMultiple (int keyCode, int count, KeyEvent event) | |||
{ | |||
if (keyCode != KeyEvent.KEYCODE_UNKNOWN || event.getAction() != KeyEvent.ACTION_MULTIPLE) | |||
return super.onKeyMultiple (keyCode, count, event); | |||
if (event.getCharacters() != null) | |||
{ | |||
int utf8Char = event.getCharacters().codePointAt (0); | |||
handleKeyDown (host, utf8Char, utf8Char); | |||
return true; | |||
} | |||
return false; | |||
} | |||
// this is here to make keyboard entry work on a Galaxy Tab2 10.1 | |||
@Override | |||
public InputConnection onCreateInputConnection (EditorInfo outAttrs) | |||
@@ -138,8 +138,8 @@ private: | |||
JUCE_DECLARE_NON_COPYABLE (Pimpl) | |||
}; | |||
DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard) | |||
: pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) | |||
DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCardStr) | |||
: pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCardStr)) | |||
{ | |||
} | |||
@@ -150,7 +150,10 @@ File File::getSpecialLocation (const SpecialLocationType type) | |||
case currentExecutableFile: | |||
case currentApplicationFile: | |||
#if ! JUCE_STANDALONE_APPLICATION | |||
return juce_getExecutableFile(); | |||
#endif | |||
// deliberate fall-through if this is not a shared-library | |||
case hostApplicationPath: | |||
{ | |||
@@ -216,7 +219,7 @@ bool Process::openDocument (const String& fileName, const String& parameters) | |||
cmdString = cmdLines.joinIntoString (" || "); | |||
} | |||
const char* const argv[4] = { "/bin/sh", "-c", cmdString.toUTF8(), 0 }; | |||
const char* const argv[4] = { "/bin/sh", "-c", cmdString.toUTF8(), nullptr }; | |||
#if JUCE_USE_VFORK | |||
const int cpid = vfork(); | |||
@@ -226,11 +229,12 @@ bool Process::openDocument (const String& fileName, const String& parameters) | |||
if (cpid == 0) | |||
{ | |||
#if ! JUCE_USE_VFORK | |||
setsid(); | |||
#endif | |||
// Child process | |||
execve (argv[0], (char**) argv, environ); | |||
exit (0); | |||
if (execvp (argv[0], (char**) argv) < 0) | |||
_exit (0); | |||
} | |||
return cpid >= 0; | |||
@@ -127,7 +127,7 @@ public: | |||
if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) | |||
return 0; // (timeout) | |||
const int bytesRead = jmax (0, (int) recv (socketHandle, buffer, bytesToRead, MSG_WAITALL)); | |||
const int bytesRead = jmax (0, (int) recv (socketHandle, buffer, (size_t) bytesToRead, MSG_WAITALL)); | |||
if (bytesRead == 0) | |||
finished = true; | |||
@@ -182,7 +182,7 @@ private: | |||
} | |||
int createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | |||
const int numRedirectsToFollow) | |||
const int numRedirects) | |||
{ | |||
closeSocket (false); | |||
@@ -193,7 +193,7 @@ private: | |||
else if (timeOutMs < 0) | |||
timeOutTime = 0xffffffff; | |||
else | |||
timeOutTime += timeOutMs; | |||
timeOutTime += (uint32) timeOutMs; | |||
String hostName, hostPath; | |||
int hostPort; | |||
@@ -279,7 +279,7 @@ private: | |||
String location (findHeaderItem (headerLines, "Location:")); | |||
if (++levelsOfRedirection <= numRedirectsToFollow | |||
if (++levelsOfRedirection <= numRedirects | |||
&& status >= 300 && status < 400 | |||
&& location.isNotEmpty() && location != address) | |||
{ | |||
@@ -295,7 +295,7 @@ private: | |||
} | |||
address = location; | |||
return createConnection (progressCallback, progressCallbackContext, numRedirectsToFollow); | |||
return createConnection (progressCallback, progressCallbackContext, numRedirects); | |||
} | |||
return status; | |||
@@ -391,12 +391,12 @@ private: | |||
const int numToSend = jmin (1024, (int) (requestHeader.getSize() - totalHeaderSent)); | |||
if (send (socketHandle, static_cast <const char*> (requestHeader.getData()) + totalHeaderSent, numToSend, 0) != numToSend) | |||
if (send (socketHandle, static_cast <const char*> (requestHeader.getData()) + totalHeaderSent, (size_t) numToSend, 0) != numToSend) | |||
return false; | |||
totalHeaderSent += numToSend; | |||
totalHeaderSent += (size_t) numToSend; | |||
if (progressCallback != nullptr && ! progressCallback (progressCallbackContext, totalHeaderSent, requestHeader.getSize())) | |||
if (progressCallback != nullptr && ! progressCallback (progressCallbackContext, (int) totalHeaderSent, (int) requestHeader.getSize())) | |||
return false; | |||
} | |||
@@ -55,10 +55,10 @@ bool SystemStats::isOperatingSystem64Bit() | |||
//============================================================================== | |||
namespace LinuxStatsHelpers | |||
{ | |||
String getCpuInfo (const char* const key) | |||
String getConfigFileValue (const char* file, const char* const key) | |||
{ | |||
StringArray lines; | |||
File ("/proc/cpuinfo").readLines (lines); | |||
File (file).readLines (lines); | |||
for (int i = lines.size(); --i >= 0;) // (NB - it's important that this runs in reverse order) | |||
if (lines[i].upToFirstOccurrenceOf (":", false, false).trim().equalsIgnoreCase (key)) | |||
@@ -66,6 +66,11 @@ namespace LinuxStatsHelpers | |||
return String(); | |||
} | |||
String getCpuInfo (const char* key) | |||
{ | |||
return getConfigFileValue ("/proc/cpuinfo", key); | |||
} | |||
} | |||
String SystemStats::getDeviceDescription() | |||
@@ -93,14 +98,14 @@ int SystemStats::getMemorySizeInMegabytes() | |||
struct sysinfo sysi; | |||
if (sysinfo (&sysi) == 0) | |||
return sysi.totalram * sysi.mem_unit / (1024 * 1024); | |||
return (int) (sysi.totalram * sysi.mem_unit / (1024 * 1024)); | |||
return 0; | |||
} | |||
int SystemStats::getPageSize() | |||
{ | |||
return sysconf (_SC_PAGESIZE); | |||
return (int) sysconf (_SC_PAGESIZE); | |||
} | |||
//============================================================================== | |||
@@ -150,6 +155,8 @@ void CPUInformation::initialise() noexcept | |||
hasSSE2 = flags.contains ("sse2"); | |||
hasSSE3 = flags.contains ("sse3"); | |||
has3DNow = flags.contains ("3dnow"); | |||
hasSSSE3 = flags.contains ("ssse3"); | |||
hasAVX = flags.contains ("avx"); | |||
numCpus = LinuxStatsHelpers::getCpuInfo ("processor").getIntValue() + 1; | |||
} | |||
@@ -160,7 +167,7 @@ uint32 juce_millisecondsSinceStartup() noexcept | |||
timespec t; | |||
clock_gettime (CLOCK_MONOTONIC, &t); | |||
return t.tv_sec * 1000 + t.tv_nsec / 1000000; | |||
return (uint32) (t.tv_sec * 1000 + t.tv_nsec / 1000000); | |||
} | |||
int64 Time::getHighResolutionTicks() noexcept | |||
@@ -189,3 +196,13 @@ bool Time::setSystemTimeToThisTime() const | |||
return settimeofday (&t, 0) == 0; | |||
} | |||
JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() | |||
{ | |||
#if JUCE_BSD | |||
return false; | |||
#else | |||
return LinuxStatsHelpers::getConfigFileValue ("/proc/self/status", "TracerPid") | |||
.getIntValue() > 0; | |||
#endif | |||
} |
@@ -52,28 +52,6 @@ JUCE_API void JUCE_CALLTYPE Process::setPriority (const ProcessPriority prior) | |||
pthread_setschedparam (pthread_self(), policy, ¶m); | |||
} | |||
JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() | |||
{ | |||
#if JUCE_BSD | |||
return false; | |||
#else | |||
static char testResult = 0; | |||
if (testResult == 0) | |||
{ | |||
testResult = (char) ptrace (PT_TRACE_ME, 0, 0, 0); | |||
if (testResult >= 0) | |||
{ | |||
ptrace (PT_DETACH, 0, (caddr_t) 1, 0); | |||
testResult = 1; | |||
} | |||
} | |||
return testResult < 0; | |||
#endif | |||
} | |||
JUCE_API bool JUCE_CALLTYPE Process::isRunningUnderDebugger() | |||
{ | |||
return juce_isRunningUnderDebugger(); | |||
@@ -110,7 +110,7 @@ namespace FileHelpers | |||
static bool launchExecutable (const String& pathAndArguments) | |||
{ | |||
const char* const argv[4] = { "/bin/sh", "-c", pathAndArguments.toUTF8(), 0 }; | |||
const char* const argv[4] = { "/bin/sh", "-c", pathAndArguments.toUTF8(), nullptr }; | |||
#if JUCE_USE_VFORK | |||
const int cpid = vfork(); | |||
@@ -121,16 +121,11 @@ namespace FileHelpers | |||
if (cpid == 0) | |||
{ | |||
// Child process | |||
if (execve (argv[0], (char**) argv, 0) < 0) | |||
exit (0); | |||
} | |||
else | |||
{ | |||
if (cpid < 0) | |||
return false; | |||
if (execvp (argv[0], (char**) argv) < 0) | |||
_exit (0); | |||
} | |||
return true; | |||
return cpid >= 0; | |||
} | |||
} | |||
@@ -128,7 +128,8 @@ public: | |||
hasFailed (false), | |||
hasFinished (false), | |||
numRedirectsToFollow (maxRedirects), | |||
numRedirects (0) | |||
numRedirects (0), | |||
latestTotalBytes (0) | |||
{ | |||
static DelegateClass cls; | |||
delegate = [cls.createInstance() init]; | |||
@@ -152,7 +153,7 @@ public: | |||
while (isThreadRunning() && ! initialised) | |||
{ | |||
if (callback != nullptr) | |||
callback (context, -1, (int) [[request HTTPBody] length]); | |||
callback (context, latestTotalBytes, (int) [[request HTTPBody] length]); | |||
Thread::sleep (1); | |||
} | |||
@@ -203,7 +204,6 @@ public: | |||
[data setLength: 0]; | |||
} | |||
initialised = true; | |||
contentLength = [response expectedContentLength]; | |||
[headers release]; | |||
@@ -215,6 +215,8 @@ public: | |||
headers = [[httpResponse allHeaderFields] retain]; | |||
statusCode = (int) [httpResponse statusCode]; | |||
} | |||
initialised = true; | |||
} | |||
NSURLRequest* willSendRequest (NSURLRequest* newRequest, NSURLResponse* redirectResponse) | |||
@@ -245,8 +247,9 @@ public: | |||
initialised = true; | |||
} | |||
void didSendBodyData (NSInteger /*totalBytesWritten*/, NSInteger /*totalBytesExpected*/) | |||
void didSendBodyData (NSInteger totalBytesWritten, NSInteger /*totalBytesExpected*/) | |||
{ | |||
latestTotalBytes = static_cast<int> (totalBytesWritten); | |||
} | |||
void finishedLoading() | |||
@@ -280,6 +283,7 @@ public: | |||
bool initialised, hasFailed, hasFinished; | |||
const int numRedirectsToFollow; | |||
int numRedirects; | |||
int latestTotalBytes; | |||
private: | |||
//============================================================================== | |||
@@ -80,6 +80,8 @@ void CPUInformation::initialise() noexcept | |||
hasSSE2 = (d & (1u << 26)) != 0; | |||
has3DNow = (b & (1u << 31)) != 0; | |||
hasSSE3 = (c & (1u << 0)) != 0; | |||
hasSSSE3 = (c & (1u << 9)) != 0; | |||
hasAVX = (c & (1u << 28)) != 0; | |||
#endif | |||
#if JUCE_IOS || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) | |||
@@ -65,14 +65,16 @@ namespace | |||
static_cast <CGFloat> (r.getWidth()), | |||
static_cast <CGFloat> (r.getHeight())); | |||
} | |||
#endif | |||
// These hacks are a workaround for newer Xcode builds which by default prevent calls to these objc functions.. | |||
typedef id (*MsgSendSuperFn) (struct objc_super*, SEL, ...); | |||
static inline MsgSendSuperFn getMsgSendSuperFn() noexcept { return (MsgSendSuperFn) (void*) objc_msgSendSuper; } | |||
#if ! JUCE_PPC | |||
typedef double (*MsgSendFPRetFn) (id, SEL op, ...); | |||
static inline MsgSendFPRetFn getMsgSendFPRetFn() noexcept { return (MsgSendFPRetFn) (void*) objc_msgSend_fpret; } | |||
#endif | |||
#endif | |||
} | |||
//============================================================================== | |||
@@ -147,11 +149,13 @@ struct ObjCClass | |||
jassert (b); (void) b; | |||
} | |||
#if JUCE_MAC | |||
static id sendSuperclassMessage (id self, SEL selector) | |||
{ | |||
objc_super s = { self, [SuperclassType class] }; | |||
return getMsgSendSuperFn() (&s, selector); | |||
} | |||
#endif | |||
template <typename Type> | |||
static Type getIvar (id self, const char* name) | |||
@@ -596,12 +596,38 @@ File juce_getExecutableFile() | |||
{ | |||
Dl_info exeInfo; | |||
dladdr ((void*) juce_getExecutableFile, &exeInfo); | |||
return CharPointer_UTF8 (exeInfo.dli_fname); | |||
const CharPointer_UTF8 filename (exeInfo.dli_fname); | |||
// if the filename is absolute simply return it | |||
if (File::isAbsolutePath (filename)) | |||
return filename; | |||
// if the filename is relative construct from CWD | |||
if (filename[0] == '.') | |||
return File::getCurrentWorkingDirectory().getChildFile (filename).getFullPathName(); | |||
// filename is abstract, look up in PATH | |||
if (const char* const envpath = ::getenv ("PATH")) | |||
{ | |||
StringArray paths (StringArray::fromTokens (envpath, ":", "")); | |||
for (int i=paths.size(); --i>=0;) | |||
{ | |||
const File filepath (File (paths[i]).getChildFile (filename)); | |||
if (filepath.existsAsFile()) | |||
return filepath.getFullPathName(); | |||
} | |||
} | |||
// if we reach this, we failed to find ourselves... | |||
jassertfalse; | |||
return filename; | |||
} | |||
}; | |||
static String filename (DLAddrReader::getFilename()); | |||
return File::getCurrentWorkingDirectory().getChildFile (filename); | |||
return filename; | |||
#endif | |||
} | |||
@@ -961,22 +987,22 @@ void JUCE_CALLTYPE Thread::setCurrentThreadAffinityMask (const uint32 affinityMa | |||
if ((affinityMask & (1 << i)) != 0) | |||
CPU_SET (i, &affinity); | |||
/* | |||
N.B. If this line causes a compile error, then you've probably not got the latest | |||
version of glibc installed. | |||
If you don't want to update your copy of glibc and don't care about cpu affinities, | |||
then you can just disable all this stuff by setting the SUPPORT_AFFINITIES macro to 0. | |||
*/ | |||
#if (! JUCE_LINUX) || ((__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2004) | |||
pthread_setaffinity_np (pthread_self(), sizeof (cpu_set_t), &affinity); | |||
#else | |||
// NB: this call isn't really correct because it sets the affinity of the process, | |||
// not the thread. But it's included here as a fallback for people who are using | |||
// ridiculously old versions of glibc | |||
sched_setaffinity (getpid(), sizeof (cpu_set_t), &affinity); | |||
#endif | |||
sched_yield(); | |||
#else | |||
/* affinities aren't supported because either the appropriate header files weren't found, | |||
or the SUPPORT_AFFINITIES macro was turned off | |||
*/ | |||
// affinities aren't supported because either the appropriate header files weren't found, | |||
// or the SUPPORT_AFFINITIES macro was turned off | |||
jassertfalse; | |||
(void) affinityMask; | |||
ignoreUnused (affinityMask); | |||
#endif | |||
} | |||
@@ -1057,8 +1083,8 @@ public: | |||
close (pipeHandles[1]); | |||
#endif | |||
execvp (argv[0], argv.getRawDataPointer()); | |||
exit (-1); | |||
if (execvp (argv[0], argv.getRawDataPointer()) < 0) | |||
_exit (-1); | |||
} | |||
else | |||
{ | |||
@@ -1285,7 +1311,7 @@ private: | |||
{ | |||
struct timespec t; | |||
clock_gettime (CLOCK_MONOTONIC, &t); | |||
time = 1000000000 * (int64) t.tv_sec + t.tv_nsec; | |||
time = (uint64) (1000000000 * (int64) t.tv_sec + (int64) t.tv_nsec); | |||
} | |||
void wait() noexcept | |||
@@ -105,6 +105,8 @@ void CPUInformation::initialise() noexcept | |||
hasSSE = (info[3] & (1 << 25)) != 0; | |||
hasSSE2 = (info[3] & (1 << 26)) != 0; | |||
hasSSE3 = (info[2] & (1 << 0)) != 0; | |||
hasAVX = (info[2] & (1 << 28)) != 0; | |||
hasSSSE3 = (info[2] & (1 << 9)) != 0; | |||
has3DNow = (info[1] & (1 << 31)) != 0; | |||
SYSTEM_INFO systemInfo; | |||
@@ -131,7 +133,12 @@ static bool isWindowsVersionOrLater (SystemStats::OperatingSystemType target) | |||
zerostruct (info); | |||
info.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX); | |||
if (target >= SystemStats::WinVista) | |||
if (target >= SystemStats::Windows10) | |||
{ | |||
info.dwMajorVersion = 10; | |||
info.dwMinorVersion = 0; | |||
} | |||
else if (target >= SystemStats::WinVista) | |||
{ | |||
info.dwMajorVersion = 6; | |||
@@ -166,7 +173,7 @@ static bool isWindowsVersionOrLater (SystemStats::OperatingSystemType target) | |||
SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() | |||
{ | |||
const SystemStats::OperatingSystemType types[] | |||
= { Windows8_1, Windows8_0, Windows7, WinVista, WinXP, Win2000 }; | |||
= { Windows10, Windows8_1, Windows8_0, Windows7, WinVista, WinXP, Win2000 }; | |||
for (int i = 0; i < numElementsInArray (types); ++i) | |||
if (isWindowsVersionOrLater (types[i])) | |||
@@ -182,6 +189,7 @@ String SystemStats::getOperatingSystemName() | |||
switch (getOperatingSystemType()) | |||
{ | |||
case Windows10: name = "Windows 10"; break; | |||
case Windows8_1: name = "Windows 8.1"; break; | |||
case Windows8_0: name = "Windows 8.0"; break; | |||
case Windows7: name = "Windows 7"; break; | |||
@@ -126,7 +126,7 @@ static void findIPAddresses (int sock, Array<IPAddress>& result) | |||
cfg.ifc_buf += IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len; | |||
} | |||
#else | |||
for (size_t i = 0; i < cfg.ifc_len / sizeof (struct ifreq); ++i) | |||
for (size_t i = 0; i < (size_t) cfg.ifc_len / (size_t) sizeof (struct ifreq); ++i) | |||
{ | |||
const ifreq& item = cfg.ifc_req[i]; | |||
@@ -75,9 +75,59 @@ namespace SocketHelpers | |||
: (setsockopt (handle, IPPROTO_TCP, TCP_NODELAY, (const char*) &one, sizeof (one)) == 0)); | |||
} | |||
static bool bindSocketToPort (const SocketHandle handle, const int port) noexcept | |||
static void closeSocket (volatile int& handle, CriticalSection& readLock, | |||
const bool isListener, int portNumber, bool& connected) noexcept | |||
{ | |||
if (handle <= 0 || port <= 0) | |||
const SocketHandle h = handle; | |||
handle = -1; | |||
#if JUCE_WINDOWS | |||
ignoreUnused (portNumber, isListener, readLock); | |||
if (h != SOCKET_ERROR || connected) | |||
closesocket (h); | |||
// make sure any read process finishes before we delete the socket | |||
CriticalSection::ScopedLockType lock(readLock); | |||
connected = false; | |||
#else | |||
if (connected) | |||
{ | |||
connected = false; | |||
if (isListener) | |||
{ | |||
// need to do this to interrupt the accept() function.. | |||
StreamingSocket temp; | |||
temp.connect (IPAddress::local().toString(), portNumber, 1000); | |||
} | |||
} | |||
if (h != -1) | |||
{ | |||
// 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 | |||
// a chance to process before close is called. On Mac OS X shutdown | |||
// does not unblock a select call, so using a lock here will dead-lock | |||
// both threads. | |||
#if JUCE_LINUX | |||
CriticalSection::ScopedLockType lock (readLock); | |||
::close (h); | |||
#else | |||
::close (h); | |||
CriticalSection::ScopedLockType lock (readLock); | |||
#endif | |||
} | |||
} | |||
#endif | |||
} | |||
static bool bindSocket (const SocketHandle handle, const int port, const String& address) noexcept | |||
{ | |||
if (handle <= 0 || port < 0) | |||
return false; | |||
struct sockaddr_in servTmpAddr; | |||
@@ -86,30 +136,64 @@ namespace SocketHelpers | |||
servTmpAddr.sin_addr.s_addr = htonl (INADDR_ANY); | |||
servTmpAddr.sin_port = htons ((uint16) port); | |||
if (address.isNotEmpty()) | |||
servTmpAddr.sin_addr.s_addr = ::inet_addr (address.toUTF8()); | |||
return bind (handle, (struct sockaddr*) &servTmpAddr, sizeof (struct sockaddr_in)) >= 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 (getsockname (handle, (struct sockaddr*) &sin_addr, &len) == 0) | |||
return ntohs (sin_addr.sin_port); | |||
return -1; | |||
} | |||
static int readSocket (const SocketHandle handle, | |||
void* const destBuffer, const int maxBytesToRead, | |||
bool volatile& connected, | |||
const bool blockUntilSpecifiedAmountHasArrived) noexcept | |||
const bool blockUntilSpecifiedAmountHasArrived, | |||
CriticalSection& readLock, | |||
String* senderIP = nullptr, | |||
int* senderPort = nullptr) noexcept | |||
{ | |||
int bytesRead = 0; | |||
while (bytesRead < maxBytesToRead) | |||
{ | |||
int bytesThisTime; | |||
long bytesThisTime = -1; | |||
char* const buffer = static_cast<char*> (destBuffer) + bytesRead; | |||
const juce_socklen_t numToRead = (juce_socklen_t) (maxBytesToRead - bytesRead); | |||
#if JUCE_WINDOWS | |||
bytesThisTime = recv (handle, static_cast<char*> (destBuffer) + bytesRead, maxBytesToRead - bytesRead, 0); | |||
#else | |||
while ((bytesThisTime = (int) ::read (handle, addBytesToPointer (destBuffer, bytesRead), | |||
(size_t) (maxBytesToRead - bytesRead))) < 0 | |||
&& errno == EINTR | |||
&& connected) | |||
{ | |||
// avoid race-condition | |||
CriticalSection::ScopedTryLockType lock (readLock); | |||
if (lock.isLocked()) | |||
{ | |||
if (senderIP == nullptr || senderPort == nullptr) | |||
{ | |||
bytesThisTime = ::recv (handle, buffer, numToRead, 0); | |||
} | |||
else | |||
{ | |||
sockaddr_in client; | |||
socklen_t clientLen = sizeof (sockaddr); | |||
bytesThisTime = ::recvfrom (handle, buffer, numToRead, 0, (sockaddr*) &client, &clientLen); | |||
*senderIP = String::fromUTF8 (inet_ntoa (client.sin_addr), 16); | |||
*senderPort = ntohs (client.sin_port); | |||
} | |||
} | |||
} | |||
#endif | |||
if (bytesThisTime <= 0 || ! connected) | |||
{ | |||
@@ -125,11 +209,20 @@ namespace SocketHelpers | |||
break; | |||
} | |||
return bytesRead; | |||
return (int) bytesRead; | |||
} | |||
static int waitForReadiness (const SocketHandle handle, const bool forReading, const int timeoutMsecs) noexcept | |||
static int waitForReadiness (const volatile int& handle, CriticalSection& readLock, | |||
const bool forReading, const int timeoutMsecs) noexcept | |||
{ | |||
// avoid race-condition | |||
CriticalSection::ScopedTryLockType lock (readLock); | |||
if (! lock.isLocked()) | |||
return -1; | |||
int h = handle; | |||
struct timeval timeout; | |||
struct timeval* timeoutp; | |||
@@ -146,20 +239,20 @@ namespace SocketHelpers | |||
fd_set rset, wset; | |||
FD_ZERO (&rset); | |||
FD_SET (handle, &rset); | |||
FD_SET (h, &rset); | |||
FD_ZERO (&wset); | |||
FD_SET (handle, &wset); | |||
FD_SET (h, &wset); | |||
fd_set* const prset = forReading ? &rset : nullptr; | |||
fd_set* const pwset = forReading ? nullptr : &wset; | |||
#if JUCE_WINDOWS | |||
if (select ((int) handle + 1, prset, pwset, 0, timeoutp) < 0) | |||
if (select ((int) h + 1, prset, pwset, 0, timeoutp) < 0) | |||
return -1; | |||
#else | |||
{ | |||
int result; | |||
while ((result = select (handle + 1, prset, pwset, 0, timeoutp)) < 0 | |||
while ((result = select (h + 1, prset, pwset, 0, timeoutp)) < 0 | |||
&& errno == EINTR) | |||
{ | |||
} | |||
@@ -169,16 +262,20 @@ namespace SocketHelpers | |||
} | |||
#endif | |||
// we are closing | |||
if (handle < 0) | |||
return -1; | |||
{ | |||
int opt; | |||
juce_socklen_t len = sizeof (opt); | |||
if (getsockopt (handle, SOL_SOCKET, SO_ERROR, (char*) &opt, &len) < 0 | |||
if (getsockopt (h, SOL_SOCKET, SO_ERROR, (char*) &opt, &len) < 0 | |||
|| opt != 0) | |||
return -1; | |||
} | |||
return FD_ISSET (handle, forReading ? &rset : &wset) ? 1 : 0; | |||
return FD_ISSET (h, forReading ? &rset : &wset) ? 1 : 0; | |||
} | |||
static bool setSocketBlockingState (const SocketHandle handle, const bool shouldBlock) noexcept | |||
@@ -201,12 +298,7 @@ namespace SocketHelpers | |||
#endif | |||
} | |||
static bool connectSocket (int volatile& handle, | |||
const bool isDatagram, | |||
struct addrinfo** const serverAddress, | |||
const String& hostName, | |||
const int portNumber, | |||
const int timeOutMillisecs) noexcept | |||
static addrinfo* getAddressInfo (const bool isDatagram, const String& hostName, int portNumber) | |||
{ | |||
struct addrinfo hints; | |||
zerostruct (hints); | |||
@@ -216,8 +308,22 @@ namespace SocketHelpers | |||
hints.ai_flags = AI_NUMERICSERV; | |||
struct addrinfo* info = nullptr; | |||
if (getaddrinfo (hostName.toUTF8(), String (portNumber).toUTF8(), &hints, &info) != 0 | |||
|| info == nullptr) | |||
if (getaddrinfo (hostName.toUTF8(), String (portNumber).toUTF8(), &hints, &info) == 0 | |||
&& info != nullptr) | |||
return info; | |||
return nullptr; | |||
} | |||
static bool connectSocket (int volatile& handle, | |||
CriticalSection& readLock, | |||
const String& hostName, | |||
const int portNumber, | |||
const int timeOutMillisecs) noexcept | |||
{ | |||
struct addrinfo* info = getAddressInfo (false, hostName, portNumber); | |||
if (info == nullptr) | |||
return false; | |||
if (handle < 0) | |||
@@ -229,19 +335,12 @@ namespace SocketHelpers | |||
return false; | |||
} | |||
if (isDatagram) | |||
{ | |||
if (*serverAddress != nullptr) | |||
freeaddrinfo (*serverAddress); | |||
*serverAddress = info; | |||
return true; | |||
} | |||
setSocketBlockingState (handle, false); | |||
const int result = ::connect (handle, info->ai_addr, (socklen_t) info->ai_addrlen); | |||
freeaddrinfo (info); | |||
bool retval = (result >= 0); | |||
if (result < 0) | |||
{ | |||
#if JUCE_WINDOWS | |||
@@ -250,18 +349,17 @@ namespace SocketHelpers | |||
if (errno == EINPROGRESS) | |||
#endif | |||
{ | |||
if (waitForReadiness (handle, false, timeOutMillisecs) != 1) | |||
{ | |||
setSocketBlockingState (handle, true); | |||
return false; | |||
} | |||
if (waitForReadiness (handle, readLock, false, timeOutMillisecs) == 1) | |||
retval = true; | |||
} | |||
} | |||
setSocketBlockingState (handle, true); | |||
resetSocketOptions (handle, false, false); | |||
return true; | |||
if (retval) | |||
resetSocketOptions (handle, false, false); | |||
return retval; | |||
} | |||
static void makeReusable (int handle) noexcept | |||
@@ -269,6 +367,23 @@ namespace SocketHelpers | |||
const int reuse = 1; | |||
setsockopt (handle, SOL_SOCKET, SO_REUSEADDR, (const char*) &reuse, sizeof (reuse)); | |||
} | |||
static bool multicast (int handle, const String& multicastIPAddress, | |||
const String& interfaceIPAddress, bool join) noexcept | |||
{ | |||
struct ip_mreq mreq; | |||
zerostruct (mreq); | |||
mreq.imr_multiaddr.s_addr = inet_addr (multicastIPAddress.toUTF8()); | |||
mreq.imr_interface.s_addr = INADDR_ANY; | |||
if (interfaceIPAddress.isNotEmpty()) | |||
mreq.imr_interface.s_addr = inet_addr (interfaceIPAddress.toUTF8()); | |||
int joinCmd = join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; | |||
return setsockopt (handle, IPPROTO_IP, joinCmd, (const char*) &mreq, sizeof (mreq)) == 0; | |||
} | |||
} | |||
//============================================================================== | |||
@@ -298,11 +413,10 @@ StreamingSocket::~StreamingSocket() | |||
} | |||
//============================================================================== | |||
int StreamingSocket::read (void* destBuffer, const int maxBytesToRead, | |||
const bool blockUntilSpecifiedAmountHasArrived) | |||
int StreamingSocket::read (void* destBuffer, const int maxBytesToRead, bool shouldBlock) | |||
{ | |||
return (connected && ! isListener) ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, | |||
connected, blockUntilSpecifiedAmountHasArrived) | |||
connected, shouldBlock, readLock) | |||
: -1; | |||
} | |||
@@ -311,32 +425,31 @@ int StreamingSocket::write (const void* sourceBuffer, const int numBytesToWrite) | |||
if (isListener || ! connected) | |||
return -1; | |||
#if JUCE_WINDOWS | |||
return send (handle, (const char*) sourceBuffer, numBytesToWrite, 0); | |||
#else | |||
int result; | |||
while ((result = (int) ::write (handle, sourceBuffer, (size_t) numBytesToWrite)) < 0 | |||
&& errno == EINTR) | |||
{ | |||
} | |||
return result; | |||
#endif | |||
return (int) ::send (handle, (const char*) sourceBuffer, (juce_socklen_t) numBytesToWrite, 0); | |||
} | |||
//============================================================================== | |||
int StreamingSocket::waitUntilReady (const bool readyForReading, | |||
const int timeoutMsecs) const | |||
{ | |||
return connected ? SocketHelpers::waitForReadiness (handle, readyForReading, timeoutMsecs) | |||
return connected ? SocketHelpers::waitForReadiness (handle, readLock, readyForReading, timeoutMsecs) | |||
: -1; | |||
} | |||
//============================================================================== | |||
bool StreamingSocket::bindToPort (const int port) | |||
{ | |||
return SocketHelpers::bindSocketToPort (handle, port); | |||
return bindToPort (port, String()); | |||
} | |||
bool StreamingSocket::bindToPort (const int port, const String& addr) | |||
{ | |||
return SocketHelpers::bindSocket (handle, port, addr); | |||
} | |||
int StreamingSocket::getBoundPort() const noexcept | |||
{ | |||
return SocketHelpers::getBoundPort (handle); | |||
} | |||
bool StreamingSocket::connect (const String& remoteHostName, | |||
@@ -356,7 +469,7 @@ bool StreamingSocket::connect (const String& remoteHostName, | |||
portNumber = remotePortNumber; | |||
isListener = false; | |||
connected = SocketHelpers::connectSocket (handle, false, nullptr, remoteHostName, | |||
connected = SocketHelpers::connectSocket (handle, readLock, remoteHostName, | |||
remotePortNumber, timeOutMillisecs); | |||
if (! (connected && SocketHelpers::resetSocketOptions (handle, false, false))) | |||
@@ -370,30 +483,7 @@ bool StreamingSocket::connect (const String& remoteHostName, | |||
void StreamingSocket::close() | |||
{ | |||
#if JUCE_WINDOWS | |||
if (handle != SOCKET_ERROR || connected) | |||
closesocket (handle); | |||
connected = false; | |||
#else | |||
if (connected) | |||
{ | |||
connected = false; | |||
if (isListener) | |||
{ | |||
// need to do this to interrupt the accept() function.. | |||
StreamingSocket temp; | |||
temp.connect (IPAddress::local().toString(), portNumber, 1000); | |||
} | |||
} | |||
if (handle != -1) | |||
{ | |||
::shutdown (handle, SHUT_RDWR); | |||
::close (handle); | |||
} | |||
#endif | |||
SocketHelpers::closeSocket (handle, readLock, isListener, portNumber, connected); | |||
hostName.clear(); | |||
portNumber = 0; | |||
@@ -470,133 +560,109 @@ bool StreamingSocket::isLocal() const noexcept | |||
//============================================================================== | |||
//============================================================================== | |||
DatagramSocket::DatagramSocket (const int localPortNumber, const bool canBroadcast) | |||
: portNumber (0), | |||
handle (-1), | |||
connected (true), | |||
allowBroadcast (canBroadcast), | |||
serverAddress (nullptr) | |||
DatagramSocket::DatagramSocket (const bool canBroadcast) | |||
: handle (-1), | |||
isBound (false), | |||
lastServerPort (-1), | |||
lastServerAddress (nullptr) | |||
{ | |||
SocketHelpers::initSockets(); | |||
handle = (int) socket (AF_INET, SOCK_DGRAM, 0); | |||
SocketHelpers::resetSocketOptions (handle, true, canBroadcast); | |||
SocketHelpers::makeReusable (handle); | |||
bindToPort (localPortNumber); | |||
} | |||
DatagramSocket::DatagramSocket (const String& host, const int portNum, | |||
const int h, const int localPortNumber) | |||
: hostName (host), | |||
portNumber (portNum), | |||
handle (h), | |||
connected (true), | |||
allowBroadcast (false), | |||
serverAddress (nullptr) | |||
{ | |||
SocketHelpers::initSockets(); | |||
SocketHelpers::resetSocketOptions (h, true, allowBroadcast); | |||
bindToPort (localPortNumber); | |||
} | |||
DatagramSocket::~DatagramSocket() | |||
{ | |||
close(); | |||
if (lastServerAddress != nullptr) | |||
freeaddrinfo (static_cast <struct addrinfo*> (lastServerAddress)); | |||
if (serverAddress != nullptr) | |||
freeaddrinfo (static_cast <struct addrinfo*> (serverAddress)); | |||
} | |||
void DatagramSocket::close() | |||
{ | |||
#if JUCE_WINDOWS | |||
closesocket (handle); | |||
connected = false; | |||
#else | |||
connected = false; | |||
::close (handle); | |||
#endif | |||
hostName.clear(); | |||
portNumber = 0; | |||
handle = -1; | |||
bool connected = false; | |||
SocketHelpers::closeSocket (handle, readLock, false, 0, connected); | |||
} | |||
bool DatagramSocket::bindToPort (const int port) | |||
{ | |||
return SocketHelpers::bindSocketToPort (handle, port); | |||
return bindToPort (port, String()); | |||
} | |||
bool DatagramSocket::connect (const String& remoteHostName, | |||
const int remotePortNumber, | |||
const int timeOutMillisecs) | |||
bool DatagramSocket::bindToPort (const int port, const String& addr) | |||
{ | |||
if (connected) | |||
close(); | |||
hostName = remoteHostName; | |||
portNumber = remotePortNumber; | |||
connected = SocketHelpers::connectSocket (handle, true, (struct addrinfo**) &serverAddress, | |||
remoteHostName, remotePortNumber, | |||
timeOutMillisecs); | |||
if (! (connected && SocketHelpers::resetSocketOptions (handle, true, allowBroadcast))) | |||
if (SocketHelpers::bindSocket (handle, port, addr)) | |||
{ | |||
close(); | |||
return false; | |||
isBound = true; | |||
lastBindAddress = addr; | |||
return true; | |||
} | |||
return true; | |||
return false; | |||
} | |||
DatagramSocket* DatagramSocket::waitForNextConnection() const | |||
int DatagramSocket::getBoundPort() const noexcept | |||
{ | |||
while (waitUntilReady (true, -1) == 1) | |||
{ | |||
struct sockaddr_storage address; | |||
juce_socklen_t len = sizeof (address); | |||
char buf[1]; | |||
if (recvfrom (handle, buf, 0, 0, (struct sockaddr*) &address, &len) > 0) | |||
return new DatagramSocket (inet_ntoa (((struct sockaddr_in*) &address)->sin_addr), | |||
ntohs (((struct sockaddr_in*) &address)->sin_port), | |||
-1, -1); | |||
} | |||
return nullptr; | |||
return isBound ? SocketHelpers::getBoundPort (handle) : -1; | |||
} | |||
//============================================================================== | |||
int DatagramSocket::waitUntilReady (const bool readyForReading, | |||
const int timeoutMsecs) const | |||
{ | |||
return connected ? SocketHelpers::waitForReadiness (handle, readyForReading, timeoutMsecs) | |||
: -1; | |||
return SocketHelpers::waitForReadiness (handle, readLock, readyForReading, timeoutMsecs); | |||
} | |||
int DatagramSocket::read (void* destBuffer, const int maxBytesToRead, const bool blockUntilSpecifiedAmountHasArrived) | |||
int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock) | |||
{ | |||
return connected ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, | |||
connected, blockUntilSpecifiedAmountHasArrived) | |||
: -1; | |||
bool connected = true; | |||
return isBound ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, | |||
connected, shouldBlock, readLock) : -1; | |||
} | |||
int DatagramSocket::write (const void* sourceBuffer, const int numBytesToWrite) | |||
int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock, String& senderIPAddress, int& senderPort) | |||
{ | |||
// You need to call connect() first to set the server address.. | |||
jassert (serverAddress != nullptr && connected); | |||
bool connected = true; | |||
return isBound ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, connected, | |||
shouldBlock, readLock, &senderIPAddress, &senderPort) : -1; | |||
} | |||
return connected ? (int) sendto (handle, (const char*) sourceBuffer, | |||
(size_t) numBytesToWrite, 0, | |||
static_cast <const struct addrinfo*> (serverAddress)->ai_addr, | |||
(juce_socklen_t) static_cast <const struct addrinfo*> (serverAddress)->ai_addrlen) | |||
: -1; | |||
int DatagramSocket::write (const String& remoteHostname, int remotePortNumber, | |||
const void* sourceBuffer, int numBytesToWrite) | |||
{ | |||
struct addrinfo*& info = reinterpret_cast <struct addrinfo*&> (lastServerAddress); | |||
// getaddrinfo can be quite slow so cache the result of the address lookup | |||
if (info == nullptr || remoteHostname != lastServerHost || remotePortNumber != lastServerPort) | |||
{ | |||
if (info != nullptr) | |||
freeaddrinfo (info); | |||
if ((info = SocketHelpers::getAddressInfo (true, remoteHostname, remotePortNumber)) == nullptr) | |||
return -1; | |||
lastServerHost = remoteHostname; | |||
lastServerPort = remotePortNumber; | |||
} | |||
return (int) ::sendto (handle, (const char*) sourceBuffer, | |||
(juce_socklen_t) numBytesToWrite, 0, | |||
info->ai_addr, (socklen_t) info->ai_addrlen); | |||
} | |||
bool DatagramSocket::isLocal() const noexcept | |||
bool DatagramSocket::joinMulticast (const String& multicastIPAddress) | |||
{ | |||
return hostName == "127.0.0.1"; | |||
if (! isBound) | |||
return false; | |||
return SocketHelpers::multicast (handle, multicastIPAddress, lastBindAddress, true); | |||
} | |||
bool DatagramSocket::leaveMulticast (const String& multicastIPAddress) | |||
{ | |||
if (! isBound) | |||
return false; | |||
return SocketHelpers::multicast (handle, multicastIPAddress, lastBindAddress, false); | |||
} | |||
#if JUCE_MSVC | |||
@@ -65,6 +65,25 @@ public: | |||
*/ | |||
bool bindToPort (int localPortNumber); | |||
/** Binds the socket to the specified local port and local address. | |||
If localAddress is not an empty string then the socket will be bound to localAddress | |||
as well. This is useful if you would like to bind your socket to a specific network | |||
adapter. Note that localAddress must be an IP address assigned to one of your | |||
network address otherwise this function will fail. | |||
@returns true on success; false may indicate that another socket is already bound | |||
on the same port | |||
@see bindToPort(int localPortNumber), IPAddress::findAllAddresses | |||
*/ | |||
bool bindToPort (int localPortNumber, const String& localAddress); | |||
/** Returns the local port number to which this socket is currently bound. | |||
This is useful if you need to know to which port the OS has actually bound your | |||
socket when calling the constructor or bindToPort with zero as the | |||
localPortNumber argument. Returns -1 if the function fails. */ | |||
int getBoundPort() const noexcept; | |||
/** Tries to connect the socket to hostname:port. | |||
If timeOutMillisecs is 0, then this method will block until the operating system | |||
@@ -164,6 +183,7 @@ private: | |||
String hostName; | |||
int volatile portNumber, handle; | |||
bool connected, isListener; | |||
mutable CriticalSection readLock; | |||
StreamingSocket (const String& hostname, int portNumber, int handle); | |||
@@ -185,22 +205,16 @@ class JUCE_API DatagramSocket | |||
public: | |||
//============================================================================== | |||
/** | |||
Creates an (uninitialised) datagram socket. | |||
Creates a datagram socket. | |||
The localPortNumber is the port on which to bind this socket. If this value is 0, | |||
the port number is assigned by the operating system. | |||
To use the socket for sending, call the connect() method. This will not immediately | |||
make a connection, but will save the destination you've provided. After this, you can | |||
call read() or write(). | |||
You first need to bind this socket to a port with bindToPort if you intend to read | |||
from this socket. | |||
If enableBroadcasting is true, the socket will be allowed to send broadcast messages | |||
(may require extra privileges on linux) | |||
To wait for other sockets to connect to this one, call waitForNextConnection(). | |||
*/ | |||
DatagramSocket (int localPortNumber, | |||
bool enableBroadcasting = false); | |||
DatagramSocket (bool enableBroadcasting = false); | |||
/** Destructor. */ | |||
~DatagramSocket(); | |||
@@ -208,37 +222,33 @@ public: | |||
//============================================================================== | |||
/** Binds the socket to the specified local port. | |||
The localPortNumber is the port on which to bind this socket. If this value is 0, | |||
the port number is assigned by the operating system. | |||
@returns true on success; false may indicate that another socket is already bound | |||
on the same port | |||
*/ | |||
bool bindToPort (int localPortNumber); | |||
/** Tries to connect the socket to hostname:port. | |||
If timeOutMillisecs is 0, then this method will block until the operating system | |||
rejects the connection (which could take a long time). | |||
/** Binds the socket to the specified local port and local address. | |||
@returns true if it succeeds. | |||
@see isConnected | |||
If localAddress is not an empty string then the socket will be bound to localAddress | |||
as well. This is useful if you would like to bind your socket to a specific network | |||
adapter. Note that localAddress must be an IP address assigned to one of your | |||
network address otherwise this function will fail. | |||
@returns true on success; false may indicate that another socket is already bound | |||
on the same port | |||
@see bindToPort(int localPortNumber), IPAddress::findAllAddresses | |||
*/ | |||
bool connect (const String& remoteHostname, | |||
int remotePortNumber, | |||
int timeOutMillisecs = 3000); | |||
bool bindToPort (int localPortNumber, const String& localAddress); | |||
/** True if the socket is currently connected. */ | |||
bool isConnected() const noexcept { return connected; } | |||
/** Returns the local port number to which this socket is currently bound. | |||
/** Closes the connection. */ | |||
void close(); | |||
/** Returns the name of the currently connected host. */ | |||
const String& getHostName() const noexcept { return hostName; } | |||
/** Returns the port number that's currently open. */ | |||
int getPort() const noexcept { return portNumber; } | |||
This is useful if you need to know to which port the OS has actually bound your | |||
socket when bindToPort was called with zero. | |||
/** True if the socket is connected to this machine rather than over the network. */ | |||
bool isLocal() const noexcept; | |||
Returns -1 if the socket didn't bind to any port yet or an error occured. */ | |||
int getBoundPort() const noexcept; | |||
/** Returns the OS's socket handle that's currently open. */ | |||
int getRawSocketHandle() const noexcept { return handle; } | |||
@@ -271,6 +281,21 @@ public: | |||
int read (void* destBuffer, int maxBytesToRead, | |||
bool blockUntilSpecifiedAmountHasArrived); | |||
/** Reads bytes from the socket and return the IP address of the sender. | |||
If blockUntilSpecifiedAmountHasArrived is true, the method will block until | |||
maxBytesToRead bytes have been read, (or until an error occurs). If this | |||
flag is false, the method will return as much data as is currently available | |||
without blocking. | |||
@returns the number of bytes read, or -1 if there was an error. On a successful | |||
result, the senderIPAddress value will be set to the IP of the sender. | |||
@see waitUntilReady | |||
*/ | |||
int read (void* destBuffer, int maxBytesToRead, | |||
bool blockUntilSpecifiedAmountHasArrived, | |||
String& senderIPAddress, int& senderPortNumber); | |||
/** Writes bytes to the socket from a buffer. | |||
Note that this method will block unless you have checked the socket is ready | |||
@@ -278,25 +303,30 @@ public: | |||
@returns the number of bytes written, or -1 if there was an error. | |||
*/ | |||
int write (const void* sourceBuffer, int numBytesToWrite); | |||
int write (const String& remoteHostname, int remotePortNumber, | |||
const void* sourceBuffer, int numBytesToWrite); | |||
//============================================================================== | |||
/** This waits for incoming data to be sent, and returns a socket that can be used | |||
to read it. | |||
/** Join a multicast group | |||
@returns true if it succeeds. | |||
*/ | |||
bool joinMulticast (const String& multicastIPAddress); | |||
The object that gets returned is owned by the caller, and can't be used for | |||
sending, but can be used to read the data. | |||
/** Leave a multicast group | |||
@returns true if it succeeds. | |||
*/ | |||
DatagramSocket* waitForNextConnection() const; | |||
bool leaveMulticast (const String& multicastIPAddress); | |||
private: | |||
//============================================================================== | |||
String hostName; | |||
int volatile portNumber, handle; | |||
bool connected, allowBroadcast; | |||
void* serverAddress; | |||
DatagramSocket (const String& hostname, int portNumber, int handle, int localPortNumber); | |||
int handle; | |||
bool isBound; | |||
String lastBindAddress, lastServerHost; | |||
int lastServerPort; | |||
void* lastServerAddress; | |||
mutable CriticalSection readLock; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DatagramSocket) | |||
}; | |||
@@ -111,7 +111,7 @@ public: | |||
capacity off the block, so that its length matches the amount of actual data that | |||
has been written so far. | |||
*/ | |||
void flush(); | |||
void flush() override; | |||
bool write (const void*, size_t) override; | |||
int64 getPosition() override { return (int64) position; } | |||
@@ -67,7 +67,8 @@ struct CPUInformation | |||
{ | |||
CPUInformation() noexcept | |||
: numCpus (0), hasMMX (false), hasSSE (false), | |||
hasSSE2 (false), hasSSE3 (false), has3DNow (false) | |||
hasSSE2 (false), hasSSE3 (false), has3DNow (false), | |||
hasSSSE3 (false), hasAVX (false) | |||
{ | |||
initialise(); | |||
} | |||
@@ -75,7 +76,7 @@ struct CPUInformation | |||
void initialise() noexcept; | |||
int numCpus; | |||
bool hasMMX, hasSSE, hasSSE2, hasSSE3, has3DNow; | |||
bool hasMMX, hasSSE, hasSSE2, hasSSE3, has3DNow, hasSSSE3, hasAVX; | |||
}; | |||
static const CPUInformation& getCPUInformation() noexcept | |||
@@ -86,10 +87,12 @@ static const CPUInformation& getCPUInformation() noexcept | |||
int SystemStats::getNumCpus() noexcept { return getCPUInformation().numCpus; } | |||
bool SystemStats::hasMMX() noexcept { return getCPUInformation().hasMMX; } | |||
bool SystemStats::has3DNow() noexcept { return getCPUInformation().has3DNow; } | |||
bool SystemStats::hasSSE() noexcept { return getCPUInformation().hasSSE; } | |||
bool SystemStats::hasSSE2() noexcept { return getCPUInformation().hasSSE2; } | |||
bool SystemStats::hasSSE3() noexcept { return getCPUInformation().hasSSE3; } | |||
bool SystemStats::has3DNow() noexcept { return getCPUInformation().has3DNow; } | |||
bool SystemStats::hasSSSE3() noexcept { return getCPUInformation().hasSSSE3; } | |||
bool SystemStats::hasAVX() noexcept { return getCPUInformation().hasAVX; } | |||
//============================================================================== | |||
@@ -47,29 +47,31 @@ public: | |||
/** The set of possible results of the getOperatingSystemType() method. */ | |||
enum OperatingSystemType | |||
{ | |||
UnknownOS = 0, | |||
Linux = 0x2000, | |||
Android = 0x3000, | |||
iOS = 0x8000, | |||
MacOSX_10_4 = 0x1004, | |||
MacOSX_10_5 = 0x1005, | |||
MacOSX_10_6 = 0x1006, | |||
MacOSX_10_7 = 0x1007, | |||
MacOSX_10_8 = 0x1008, | |||
MacOSX_10_9 = 0x1009, | |||
MacOSX_10_10 = 0x100a, | |||
Win2000 = 0x4105, | |||
WinXP = 0x4106, | |||
WinVista = 0x4107, | |||
Windows7 = 0x4108, | |||
Windows8_0 = 0x4109, | |||
Windows8_1 = 0x410a, | |||
Windows = 0x4000, /**< To test whether any version of Windows is running, | |||
you can use the expression ((getOperatingSystemType() & Windows) != 0). */ | |||
UnknownOS = 0, | |||
MacOSX = 0x0100, /**< To test whether any version of OSX is running, | |||
you can use the expression ((getOperatingSystemType() & MacOSX) != 0). */ | |||
Windows = 0x0200, /**< To test whether any version of Windows is running, | |||
you can use the expression ((getOperatingSystemType() & Windows) != 0). */ | |||
Linux = 0x0400, | |||
Android = 0x0800, | |||
iOS = 0x1000, | |||
MacOSX_10_4 = MacOSX | 4, | |||
MacOSX_10_5 = MacOSX | 5, | |||
MacOSX_10_6 = MacOSX | 6, | |||
MacOSX_10_7 = MacOSX | 7, | |||
MacOSX_10_8 = MacOSX | 8, | |||
MacOSX_10_9 = MacOSX | 9, | |||
MacOSX_10_10 = MacOSX | 10, | |||
Win2000 = Windows | 1, | |||
WinXP = Windows | 2, | |||
WinVista = Windows | 3, | |||
Windows7 = Windows | 4, | |||
Windows8_0 = Windows | 5, | |||
Windows8_1 = Windows | 6, | |||
Windows10 = Windows | 7 | |||
}; | |||
/** Returns the type of operating system we're running on. | |||
@@ -150,10 +152,12 @@ 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 has3DNow() noexcept; /**< Returns true if AMD 3DNOW 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. */ | |||
//============================================================================== | |||
/** Finds out how much RAM is in the machine. | |||
@@ -511,7 +511,7 @@ public: | |||
if (byte < 0) | |||
{ | |||
uint8 bit = 0x40; | |||
int bit = 0x40; | |||
int numExtraValues = 0; | |||
while ((byte & bit) != 0) | |||
@@ -34,18 +34,18 @@ | |||
juce_wchar CharacterFunctions::toUpperCase (const juce_wchar character) noexcept | |||
{ | |||
return towupper ((wchar_t) character); | |||
return (juce_wchar) towupper ((wint_t) character); | |||
} | |||
juce_wchar CharacterFunctions::toLowerCase (const juce_wchar character) noexcept | |||
{ | |||
return towlower ((wchar_t) character); | |||
return (juce_wchar) towlower ((wint_t) character); | |||
} | |||
bool CharacterFunctions::isUpperCase (const juce_wchar character) noexcept | |||
{ | |||
#if JUCE_WINDOWS | |||
return iswupper ((wchar_t) character) != 0; | |||
return iswupper ((wint_t) character) != 0; | |||
#else | |||
return toLowerCase (character) != character; | |||
#endif | |||
@@ -54,7 +54,7 @@ bool CharacterFunctions::isUpperCase (const juce_wchar character) noexcept | |||
bool CharacterFunctions::isLowerCase (const juce_wchar character) noexcept | |||
{ | |||
#if JUCE_WINDOWS | |||
return iswlower ((wchar_t) character) != 0; | |||
return iswlower ((wint_t) character) != 0; | |||
#else | |||
return toUpperCase (character) != character; | |||
#endif | |||
@@ -72,7 +72,7 @@ bool CharacterFunctions::isWhitespace (const char character) noexcept | |||
bool CharacterFunctions::isWhitespace (const juce_wchar character) noexcept | |||
{ | |||
return iswspace ((wchar_t) character) != 0; | |||
return iswspace ((wint_t) character) != 0; | |||
} | |||
bool CharacterFunctions::isDigit (const char character) noexcept | |||
@@ -82,7 +82,7 @@ bool CharacterFunctions::isDigit (const char character) noexcept | |||
bool CharacterFunctions::isDigit (const juce_wchar character) noexcept | |||
{ | |||
return iswdigit ((wchar_t) character) != 0; | |||
return iswdigit ((wint_t) character) != 0; | |||
} | |||
bool CharacterFunctions::isLetter (const char character) noexcept | |||
@@ -93,7 +93,7 @@ bool CharacterFunctions::isLetter (const char character) noexcept | |||
bool CharacterFunctions::isLetter (const juce_wchar character) noexcept | |||
{ | |||
return iswalpha ((wchar_t) character) != 0; | |||
return iswalpha ((wint_t) character) != 0; | |||
} | |||
bool CharacterFunctions::isLetterOrDigit (const char character) noexcept | |||
@@ -105,7 +105,7 @@ bool CharacterFunctions::isLetterOrDigit (const char character) noexcept | |||
bool CharacterFunctions::isLetterOrDigit (const juce_wchar character) noexcept | |||
{ | |||
return iswalnum ((wchar_t) character) != 0; | |||
return iswalnum ((wint_t) character) != 0; | |||
} | |||
int CharacterFunctions::getHexDigitValue (const juce_wchar digit) noexcept | |||
@@ -40,25 +40,22 @@ Identifier& Identifier::operator= (const Identifier other) noexcept | |||
Identifier::Identifier (const String& nm) | |||
: name (StringPool::getGlobalPool().getPooledString (nm)) | |||
{ | |||
/* An Identifier string must be suitable for use as a script variable or XML | |||
attribute, so it can only contain this limited set of characters.. */ | |||
jassert (isValidIdentifier (toString())); | |||
// An Identifier cannot be created from an empty string! | |||
jassert (nm.isNotEmpty()); | |||
} | |||
Identifier::Identifier (const char* nm) | |||
: name (StringPool::getGlobalPool().getPooledString (nm)) | |||
{ | |||
/* An Identifier string must be suitable for use as a script variable or XML | |||
attribute, so it can only contain this limited set of characters.. */ | |||
jassert (isValidIdentifier (toString())); | |||
// An Identifier cannot be created from an empty string! | |||
jassert (nm != nullptr && nm[0] != 0); | |||
} | |||
Identifier::Identifier (String::CharPointerType start, String::CharPointerType end) | |||
: name (StringPool::getGlobalPool().getPooledString (start, end)) | |||
{ | |||
/* An Identifier string must be suitable for use as a script variable or XML | |||
attribute, so it can only contain this limited set of characters.. */ | |||
jassert (isValidIdentifier (toString())); | |||
// An Identifier cannot be created from an empty string! | |||
jassert (start < end); | |||
} | |||
Identifier Identifier::null; | |||
@@ -1617,10 +1617,10 @@ String String::upToLastOccurrenceOf (StringRef sub, | |||
bool String::isQuotedString() const | |||
{ | |||
const String trimmed (trimStart()); | |||
const juce_wchar trimmedStart = trimStart()[0]; | |||
return trimmed[0] == '"' | |||
|| trimmed[0] == '\''; | |||
return trimmedStart == '"' | |||
|| trimmedStart == '\''; | |||
} | |||
String String::unquoted() const | |||
@@ -2522,14 +2522,13 @@ public: | |||
beginTest ("var"); | |||
var v1 = 0; | |||
var v2 = 0.1; | |||
var v3 = "0.1"; | |||
var v2 = 0.16; | |||
var v3 = "0.16"; | |||
var v4 = (int64) 0; | |||
var v5 = 0.0; | |||
expect (! v2.equals (v1)); | |||
expect (! v1.equals (v2)); | |||
expect (v2.equals (v3)); | |||
expect (v3.equals (v2)); | |||
expect (! v3.equals (v1)); | |||
expect (! v1.equals (v3)); | |||
expect (v1.equals (v4)); | |||
@@ -299,13 +299,13 @@ public: | |||
Note that there's also an isNotEmpty() method to help write readable code. | |||
@see containsNonWhitespaceChars() | |||
*/ | |||
inline bool isEmpty() const noexcept { return text[0] == 0; } | |||
inline bool isEmpty() const noexcept { return text.isEmpty(); } | |||
/** Returns true if the string contains at least one character. | |||
Note that there's also an isEmpty() method to help write readable code. | |||
@see containsNonWhitespaceChars() | |||
*/ | |||
inline bool isNotEmpty() const noexcept { return text[0] != 0; } | |||
inline bool isNotEmpty() const noexcept { return ! text.isEmpty(); } | |||
/** Resets this string to be empty. */ | |||
void clear() noexcept; | |||
@@ -235,7 +235,7 @@ public: | |||
/** Finds the thread object that is currently running. | |||
Note that the main UI thread (or other non-Juce threads) don't have a Thread | |||
object associated with them, so this will return 0. | |||
object associated with them, so this will return nullptr. | |||
*/ | |||
static Thread* JUCE_CALLTYPE getCurrentThread(); | |||
@@ -352,7 +352,7 @@ public: | |||
/** Returns the value of a named attribute as floating-point. | |||
This will try to find the attribute and convert it to an integer (using | |||
This will try to find the attribute and convert it to a double (using | |||
the String::getDoubleValue() method). | |||
@param attributeName the name of the attribute to look up | |||
@@ -72,7 +72,7 @@ public: | |||
the stream is closed - this means that no more data can be written to it, and any | |||
subsequent attempts to call write() will cause an assertion. | |||
*/ | |||
void flush(); | |||
void flush() override; | |||
int64 getPosition() override; | |||
bool setPosition (int64) override; | |||
@@ -90,7 +90,7 @@ namespace zlibNamespace | |||
class GZIPDecompressorInputStream::GZIPDecompressHelper | |||
{ | |||
public: | |||
GZIPDecompressHelper (const bool dontWrap) | |||
GZIPDecompressHelper (Format f) | |||
: finished (true), | |||
needsDictionary (false), | |||
error (true), | |||
@@ -100,7 +100,7 @@ public: | |||
{ | |||
using namespace zlibNamespace; | |||
zerostruct (stream); | |||
streamIsValid = (inflateInit2 (&stream, dontWrap ? -MAX_WBITS : MAX_WBITS) == Z_OK); | |||
streamIsValid = (inflateInit2 (&stream, getBitsForFormat (f)) == Z_OK); | |||
finished = error = ! streamIsValid; | |||
} | |||
@@ -157,6 +157,19 @@ public: | |||
return 0; | |||
} | |||
static int getBitsForFormat (Format f) noexcept | |||
{ | |||
switch (f) | |||
{ | |||
case zlibFormat: return MAX_WBITS; | |||
case deflateFormat: return -MAX_WBITS; | |||
case gzipFormat: return MAX_WBITS | 16; | |||
default: jassertfalse; break; | |||
} | |||
return MAX_WBITS; | |||
} | |||
bool finished, needsDictionary, error, streamIsValid; | |||
enum { gzipDecompBufferSize = 32768 }; | |||
@@ -170,32 +183,30 @@ private: | |||
}; | |||
//============================================================================== | |||
GZIPDecompressorInputStream::GZIPDecompressorInputStream (InputStream* const source, | |||
const bool deleteSourceWhenDestroyed, | |||
const bool noWrap_, | |||
const int64 uncompressedStreamLength_) | |||
GZIPDecompressorInputStream::GZIPDecompressorInputStream (InputStream* source, bool deleteSourceWhenDestroyed, | |||
Format f, int64 uncompressedLength) | |||
: sourceStream (source, deleteSourceWhenDestroyed), | |||
uncompressedStreamLength (uncompressedStreamLength_), | |||
noWrap (noWrap_), | |||
uncompressedStreamLength (uncompressedLength), | |||
format (f), | |||
isEof (false), | |||
activeBufferSize (0), | |||
originalSourcePos (source->getPosition()), | |||
currentPos (0), | |||
buffer ((size_t) GZIPDecompressHelper::gzipDecompBufferSize), | |||
helper (new GZIPDecompressHelper (noWrap_)) | |||
helper (new GZIPDecompressHelper (f)) | |||
{ | |||
} | |||
GZIPDecompressorInputStream::GZIPDecompressorInputStream (InputStream& source) | |||
: sourceStream (&source, false), | |||
uncompressedStreamLength (-1), | |||
noWrap (false), | |||
format (zlibFormat), | |||
isEof (false), | |||
activeBufferSize (0), | |||
originalSourcePos (source.getPosition()), | |||
currentPos (0), | |||
buffer ((size_t) GZIPDecompressHelper::gzipDecompBufferSize), | |||
helper (new GZIPDecompressHelper (false)) | |||
helper (new GZIPDecompressHelper (zlibFormat)) | |||
{ | |||
} | |||
@@ -278,7 +289,7 @@ bool GZIPDecompressorInputStream::setPosition (int64 newPos) | |||
isEof = false; | |||
activeBufferSize = 0; | |||
currentPos = 0; | |||
helper = new GZIPDecompressHelper (noWrap); | |||
helper = new GZIPDecompressHelper (format); | |||
sourceStream->setPosition (originalSourcePos); | |||
} | |||
@@ -43,21 +43,28 @@ | |||
class JUCE_API GZIPDecompressorInputStream : public InputStream | |||
{ | |||
public: | |||
enum Format | |||
{ | |||
zlibFormat = 0, | |||
deflateFormat, | |||
gzipFormat | |||
}; | |||
//============================================================================== | |||
/** Creates a decompressor stream. | |||
@param sourceStream the stream to read from | |||
@param deleteSourceWhenDestroyed whether or not to delete the source stream | |||
when this object is destroyed | |||
@param noWrap this is used internally by the ZipFile class | |||
and should be ignored by user applications | |||
@param sourceFormat can be used to select which of the supported | |||
formats the data is expected to be in | |||
@param uncompressedStreamLength if the creator knows the length that the | |||
uncompressed stream will be, then it can supply this | |||
value, which will be returned by getTotalLength() | |||
*/ | |||
GZIPDecompressorInputStream (InputStream* sourceStream, | |||
bool deleteSourceWhenDestroyed, | |||
bool noWrap = false, | |||
Format sourceFormat = zlibFormat, | |||
int64 uncompressedStreamLength = -1); | |||
/** Creates a decompressor stream. | |||
@@ -81,7 +88,7 @@ private: | |||
//============================================================================== | |||
OptionalScopedPointer<InputStream> sourceStream; | |||
const int64 uncompressedStreamLength; | |||
const bool noWrap; | |||
const Format format; | |||
bool isEof; | |||
int activeBufferSize; | |||
int64 originalSourcePos, currentPos; | |||
@@ -91,6 +98,11 @@ private: | |||
friend struct ContainerDeletePolicy<GZIPDecompressHelper>; | |||
ScopedPointer<GZIPDecompressHelper> helper; | |||
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE | |||
// The arguments to this method have changed! Please pass a Format enum instead of the old dontWrap bool. | |||
GZIPDecompressorInputStream (InputStream*, bool, bool, int64 x = -1); | |||
#endif | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GZIPDecompressorInputStream) | |||
}; | |||
@@ -298,7 +298,9 @@ InputStream* ZipFile::createStreamForEntry (const int index) | |||
if (zei->compressed) | |||
{ | |||
stream = new GZIPDecompressorInputStream (stream, true, true, (int64) zei->entry.uncompressedSize); | |||
stream = new GZIPDecompressorInputStream (stream, true, | |||
GZIPDecompressorInputStream::deflateFormat, | |||
(int64) zei->entry.uncompressedSize); | |||
// (much faster to unzip in big blocks using a buffer..) | |||
stream = new BufferedInputStream (stream, 32768, true); | |||
@@ -571,7 +571,7 @@ local void gen_codes (ct_data *tree, /* the tree to decorate */ | |||
ushf *bl_count) /* number of codes at each bit length */ | |||
{ | |||
ush next_code[MAX_BITS+1]; /* next code value for each bit length */ | |||
ush code = 0; /* running code value */ | |||
ush code_ = 0; /* running code value */ | |||
int bits; /* bit index */ | |||
int n; /* code index */ | |||
@@ -579,12 +579,12 @@ local void gen_codes (ct_data *tree, /* the tree to decorate */ | |||
* without bit reversal. | |||
*/ | |||
for (bits = 1; bits <= MAX_BITS; bits++) { | |||
next_code[bits] = code = (code + bl_count[bits-1]) << 1; | |||
next_code[bits] = code_ = (code_ + bl_count[bits-1]) << 1; | |||
} | |||
/* Check that the bit counts in bl_count are consistent. The last code | |||
* must be all ones. | |||
*/ | |||
Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1, | |||
Assert (code_ + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1, | |||
"inconsistent bit counts"); | |||
Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); | |||
@@ -1055,7 +1055,7 @@ local void compress_block (deflate_state *s, | |||
unsigned dist; /* distance of matched string */ | |||
int lc; /* match length or unmatched char (if dist == 0) */ | |||
unsigned lx = 0; /* running index in l_buf */ | |||
unsigned code; /* the code to send */ | |||
unsigned code_; /* the code to send */ | |||
int extra; /* number of extra bits to send */ | |||
if (s->last_lit != 0) do { | |||
@@ -1066,21 +1066,21 @@ local void compress_block (deflate_state *s, | |||
Tracecv(isgraph(lc), (stderr," '%c' ", lc)); | |||
} else { | |||
/* Here, lc is the match length - MIN_MATCH */ | |||
code = _length_code[lc]; | |||
send_code(s, code+LITERALS+1, ltree); /* send the length code */ | |||
extra = extra_lbits[code]; | |||
code_ = _length_code[lc]; | |||
send_code(s, code_+LITERALS+1, ltree); /* send the length code */ | |||
extra = extra_lbits[code_]; | |||
if (extra != 0) { | |||
lc -= base_length[code]; | |||
lc -= base_length[code_]; | |||
send_bits(s, lc, extra); /* send the extra length bits */ | |||
} | |||
dist--; /* dist is now the match distance - 1 */ | |||
code = d_code(dist); | |||
Assert (code < D_CODES, "bad d_code"); | |||
code_ = d_code(dist); | |||
Assert (code_ < D_CODES, "bad d_code"); | |||
send_code(s, code, dtree); /* send the distance code */ | |||
extra = extra_dbits[code]; | |||
send_code(s, code_, dtree); /* send the distance code */ | |||
extra = extra_dbits[code_]; | |||
if (extra != 0) { | |||
dist -= base_dist[code]; | |||
dist -= base_dist[code_]; | |||
send_bits(s, dist, extra); /* send the extra distance bits */ | |||
} | |||
} /* literal or match pair ? */ | |||
@@ -1120,12 +1120,12 @@ local void set_data_type (deflate_state *s) | |||
* method would use a table) | |||
* IN assertion: 1 <= len <= 15 | |||
*/ | |||
local unsigned bi_reverse (unsigned code, int len) | |||
local unsigned bi_reverse (unsigned code_, int len) | |||
{ | |||
register unsigned res = 0; | |||
do { | |||
res |= code & 1; | |||
code >>= 1, res <<= 1; | |||
res |= code_ & 1; | |||
code_ >>= 1, res <<= 1; | |||
} while (--len > 0); | |||
return res >> 1; | |||
} | |||
@@ -223,7 +223,7 @@ public: | |||
protected: | |||
/** @internal */ | |||
virtual void propertyChanged(); | |||
void propertyChanged() override; | |||
private: | |||
//============================================================================== | |||
@@ -203,6 +203,14 @@ void UndoManager::setCurrentTransactionName (const String& newName) noexcept | |||
action->name = newName; | |||
} | |||
String UndoManager::getCurrentTransactionName() const noexcept | |||
{ | |||
if (ActionSet* action = getCurrentSet()) | |||
return action->name; | |||
return newTransactionName; | |||
} | |||
//============================================================================== | |||
UndoManager::ActionSet* UndoManager::getCurrentSet() const noexcept { return transactions [nextIndex - 1]; } | |||
UndoManager::ActionSet* UndoManager::getNextSet() const noexcept { return transactions [nextIndex]; } | |||
@@ -144,6 +144,11 @@ public: | |||
*/ | |||
void setCurrentTransactionName (const String& newName) noexcept; | |||
/** Returns the name of the current transaction. | |||
@see setCurrentTransactionName | |||
*/ | |||
String getCurrentTransactionName() const noexcept; | |||
//============================================================================== | |||
/** Returns true if there's at least one action in the list to undo. | |||
@see getUndoDescription, undo, canRedo | |||
@@ -72,12 +72,12 @@ public: | |||
{ | |||
} | |||
var getValue() const | |||
var getValue() const override | |||
{ | |||
return value; | |||
} | |||
void setValue (const var& newValue) | |||
void setValue (const var& newValue) override | |||
{ | |||
if (! newValue.equalsWithSameType (value)) | |||
{ | |||
@@ -814,8 +814,8 @@ public: | |||
tree.removeListener (this); | |||
} | |||
var getValue() const { return tree [property]; } | |||
void setValue (const var& newValue) { tree.setProperty (property, newValue, undoManager); } | |||
var getValue() const override { return tree [property]; } | |||
void setValue (const var& newValue) override { tree.setProperty (property, newValue, undoManager); } | |||
private: | |||
ValueTree tree; | |||
@@ -1003,9 +1003,16 @@ ValueTree ValueTree::readFromStream (InputStream& input) | |||
for (int i = 0; i < numProps; ++i) | |||
{ | |||
const String name (input.readString()); | |||
jassert (name.isNotEmpty()); | |||
const var value (var::readFromStream (input)); | |||
v.object->properties.set (name, value); | |||
if (name.isNotEmpty()) | |||
{ | |||
const var value (var::readFromStream (input)); | |||
v.object->properties.set (name, value); | |||
} | |||
else | |||
{ | |||
jassertfalse; // trying to read corrupted data! | |||
} | |||
} | |||
const int numChildren = input.readCompressedInt(); | |||
@@ -1015,6 +1022,9 @@ ValueTree ValueTree::readFromStream (InputStream& input) | |||
{ | |||
ValueTree child (readFromStream (input)); | |||
if (! child.isValid()) | |||
return v; | |||
v.object->children.add (child.object); | |||
child.object->parent = v.object; | |||
} | |||
@@ -232,7 +232,7 @@ int JUCEApplicationBase::main() | |||
jassert (app != nullptr); | |||
if (! app->initialiseApp()) | |||
return app->getApplicationReturnValue(); | |||
return app->shutdownApp(); | |||
JUCE_TRY | |||
{ | |||
@@ -259,6 +259,7 @@ public: | |||
static CreateInstanceFunction createInstance; | |||
virtual bool initialiseApp(); | |||
int shutdownApp(); | |||
static void JUCE_CALLTYPE sendUnhandledException (const std::exception*, const char* sourceFile, int lineNumber); | |||
bool sendCommandLineToPreexistingInstance(); | |||
#endif | |||
@@ -274,8 +275,6 @@ private: | |||
friend struct ContainerDeletePolicy<MultipleInstanceHandler>; | |||
ScopedPointer<MultipleInstanceHandler> multipleInstanceHandler; | |||
int shutdownApp(); | |||
JUCE_DECLARE_NON_COPYABLE (JUCEApplicationBase) | |||
}; | |||
@@ -25,7 +25,15 @@ | |||
void MessageManager::runDispatchLoop() | |||
{ | |||
jassert (isThisTheMessageThread()); // must only be called by the message thread | |||
runDispatchLoopUntil (-1); | |||
while (! quitMessagePosted) | |||
{ | |||
JUCE_AUTORELEASEPOOL | |||
{ | |||
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode | |||
beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.001]]; | |||
} | |||
} | |||
} | |||
void MessageManager::stopDispatchLoop() | |||
@@ -34,6 +42,7 @@ void MessageManager::stopDispatchLoop() | |||
exit (0); // iOS apps get no mercy.. | |||
} | |||
#if JUCE_MODAL_LOOPS_PERMITTED | |||
bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) | |||
{ | |||
JUCE_AUTORELEASEPOOL | |||
@@ -59,6 +68,7 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) | |||
return ! quitMessagePosted; | |||
} | |||
} | |||
#endif | |||
//============================================================================== | |||
static ScopedPointer<MessageQueue> messageQueue; | |||
@@ -74,7 +74,7 @@ public: | |||
ScopedUnlock ul (lock); | |||
const unsigned char x = 0xff; | |||
size_t bytesWritten = write (fd[0], &x, 1); | |||
ssize_t bytesWritten = write (fd[0], &x, 1); | |||
(void) bytesWritten; | |||
} | |||
} | |||
@@ -186,7 +186,7 @@ private: | |||
const ScopedUnlock ul (lock); | |||
unsigned char x; | |||
size_t numBytes = read (fd[1], &x, 1); | |||
ssize_t numBytes = read (fd[1], &x, 1); | |||
(void) numBytes; | |||
} | |||
@@ -235,6 +235,8 @@ namespace LinuxErrorHandling | |||
int errorHandler (Display* display, XErrorEvent* event) | |||
{ | |||
(void) display; (void) event; | |||
#if JUCE_DEBUG_XERRORS | |||
char errorStr[64] = { 0 }; | |||
char requestStr[64] = { 0 }; | |||
@@ -261,11 +261,13 @@ static void shutdownNSApp() | |||
void MessageManager::stopDispatchLoop() | |||
{ | |||
#if JUCE_PROJUCER_LIVE_BUILD | |||
quitMessagePosted = true; | |||
#else | |||
#if ! JUCE_PROJUCER_LIVE_BUILD | |||
if (isThisTheMessageThread()) | |||
{ | |||
quitMessagePosted = true; | |||
shutdownNSApp(); | |||
} | |||
else | |||
@@ -273,7 +275,7 @@ void MessageManager::stopDispatchLoop() | |||
struct QuitCallback : public CallbackMessage | |||
{ | |||
QuitCallback() {} | |||
void messageCallback() override { shutdownNSApp(); } | |||
void messageCallback() override { MessageManager::getInstance()->stopDispatchLoop(); } | |||
}; | |||
(new QuitCallback())->post(); | |||
@@ -112,10 +112,10 @@ public: | |||
{ | |||
const LockType::ScopedLockType sl (lock); | |||
while (firstTimer != nullptr && firstTimer->countdownMs <= 0) | |||
while (firstTimer != nullptr && firstTimer->timerCountdownMs <= 0) | |||
{ | |||
Timer* const t = firstTimer; | |||
t->countdownMs = t->periodMs; | |||
t->timerCountdownMs = t->timerPeriodMs; | |||
removeTimer (t); | |||
addTimer (t); | |||
@@ -169,11 +169,11 @@ public: | |||
{ | |||
if (instance != nullptr) | |||
{ | |||
tim->countdownMs = newCounter; | |||
tim->periodMs = newCounter; | |||
tim->timerCountdownMs = newCounter; | |||
tim->timerPeriodMs = newCounter; | |||
if ((tim->next != nullptr && tim->next->countdownMs < tim->countdownMs) | |||
|| (tim->previous != nullptr && tim->previous->countdownMs > tim->countdownMs)) | |||
if ((tim->nextTimer != nullptr && tim->nextTimer->timerCountdownMs < tim->timerCountdownMs) | |||
|| (tim->previousTimer != nullptr && tim->previousTimer->timerCountdownMs > tim->timerCountdownMs)) | |||
{ | |||
instance->removeTimer (tim); | |||
instance->addTimer (tim); | |||
@@ -210,28 +210,28 @@ private: | |||
Timer* i = firstTimer; | |||
if (i == nullptr || i->countdownMs > t->countdownMs) | |||
if (i == nullptr || i->timerCountdownMs > t->timerCountdownMs) | |||
{ | |||
t->next = firstTimer; | |||
t->nextTimer = firstTimer; | |||
firstTimer = t; | |||
} | |||
else | |||
{ | |||
while (i->next != nullptr && i->next->countdownMs <= t->countdownMs) | |||
i = i->next; | |||
while (i->nextTimer != nullptr && i->nextTimer->timerCountdownMs <= t->timerCountdownMs) | |||
i = i->nextTimer; | |||
jassert (i != nullptr); | |||
t->next = i->next; | |||
t->previous = i; | |||
i->next = t; | |||
t->nextTimer = i->nextTimer; | |||
t->previousTimer = i; | |||
i->nextTimer = t; | |||
} | |||
if (t->next != nullptr) | |||
t->next->previous = t; | |||
if (t->nextTimer != nullptr) | |||
t->nextTimer->previousTimer = t; | |||
jassert ((t->next == nullptr || t->next->countdownMs >= t->countdownMs) | |||
&& (t->previous == nullptr || t->previous->countdownMs <= t->countdownMs)); | |||
jassert ((t->nextTimer == nullptr || t->nextTimer->timerCountdownMs >= t->timerCountdownMs) | |||
&& (t->previousTimer == nullptr || t->previousTimer->timerCountdownMs <= t->timerCountdownMs)); | |||
notify(); | |||
} | |||
@@ -244,32 +244,32 @@ private: | |||
jassert (timerExists (t)); | |||
#endif | |||
if (t->previous != nullptr) | |||
if (t->previousTimer != nullptr) | |||
{ | |||
jassert (firstTimer != t); | |||
t->previous->next = t->next; | |||
t->previousTimer->nextTimer = t->nextTimer; | |||
} | |||
else | |||
{ | |||
jassert (firstTimer == t); | |||
firstTimer = t->next; | |||
firstTimer = t->nextTimer; | |||
} | |||
if (t->next != nullptr) | |||
t->next->previous = t->previous; | |||
if (t->nextTimer != nullptr) | |||
t->nextTimer->previousTimer = t->previousTimer; | |||
t->next = nullptr; | |||
t->previous = nullptr; | |||
t->nextTimer = nullptr; | |||
t->previousTimer = nullptr; | |||
} | |||
int getTimeUntilFirstTimer (const int numMillisecsElapsed) const | |||
{ | |||
const LockType::ScopedLockType sl (lock); | |||
for (Timer* t = firstTimer; t != nullptr; t = t->next) | |||
t->countdownMs -= numMillisecsElapsed; | |||
for (Timer* t = firstTimer; t != nullptr; t = t->nextTimer) | |||
t->timerCountdownMs -= numMillisecsElapsed; | |||
return firstTimer != nullptr ? firstTimer->countdownMs : 1000; | |||
return firstTimer != nullptr ? firstTimer->timerCountdownMs : 1000; | |||
} | |||
void handleAsyncUpdate() override | |||
@@ -280,7 +280,7 @@ private: | |||
#if JUCE_DEBUG | |||
bool timerExists (Timer* const t) const noexcept | |||
{ | |||
for (Timer* tt = firstTimer; tt != nullptr; tt = tt->next) | |||
for (Timer* tt = firstTimer; tt != nullptr; tt = tt->nextTimer) | |||
if (tt == t) | |||
return true; | |||
@@ -296,18 +296,18 @@ Timer::TimerThread::LockType Timer::TimerThread::lock; | |||
//============================================================================== | |||
Timer::Timer() noexcept | |||
: countdownMs (0), | |||
periodMs (0), | |||
previous (nullptr), | |||
next (nullptr) | |||
: timerCountdownMs (0), | |||
timerPeriodMs (0), | |||
previousTimer (nullptr), | |||
nextTimer (nullptr) | |||
{ | |||
} | |||
Timer::Timer (const Timer&) noexcept | |||
: countdownMs (0), | |||
periodMs (0), | |||
previous (nullptr), | |||
next (nullptr) | |||
: timerCountdownMs (0), | |||
timerPeriodMs (0), | |||
previousTimer (nullptr), | |||
nextTimer (nullptr) | |||
{ | |||
} | |||
@@ -320,10 +320,10 @@ void Timer::startTimer (const int interval) noexcept | |||
{ | |||
const TimerThread::LockType::ScopedLockType sl (TimerThread::lock); | |||
if (periodMs == 0) | |||
if (timerPeriodMs == 0) | |||
{ | |||
countdownMs = interval; | |||
periodMs = jmax (1, interval); | |||
timerCountdownMs = interval; | |||
timerPeriodMs = jmax (1, interval); | |||
TimerThread::add (this); | |||
} | |||
else | |||
@@ -344,10 +344,10 @@ void Timer::stopTimer() noexcept | |||
{ | |||
const TimerThread::LockType::ScopedLockType sl (TimerThread::lock); | |||
if (periodMs > 0) | |||
if (timerPeriodMs > 0) | |||
{ | |||
TimerThread::remove (this); | |||
periodMs = 0; | |||
timerPeriodMs = 0; | |||
} | |||
} | |||
@@ -54,7 +54,6 @@ class JUCE_API Timer | |||
protected: | |||
//============================================================================== | |||
/** Creates a Timer. | |||
When created, the timer is stopped, so use startTimer() to get it going. | |||
*/ | |||
Timer() noexcept; | |||
@@ -64,7 +63,7 @@ protected: | |||
Note that this timer won't be started, even if the one you're copying | |||
is running. | |||
*/ | |||
Timer (const Timer& other) noexcept; | |||
Timer (const Timer&) noexcept; | |||
public: | |||
//============================================================================== | |||
@@ -86,8 +85,8 @@ public: | |||
time between calling this method and the next timer callback | |||
will not be less than the interval length passed in. | |||
@param intervalInMilliseconds the interval to use (any values less than 1 will be | |||
rounded up to 1) | |||
@param intervalInMilliseconds the interval to use (any value less | |||
than 1 will be rounded up to 1) | |||
*/ | |||
void startTimer (int intervalInMilliseconds) noexcept; | |||
@@ -108,12 +107,12 @@ public: | |||
//============================================================================== | |||
/** Returns true if the timer is currently running. */ | |||
bool isTimerRunning() const noexcept { return periodMs > 0; } | |||
bool isTimerRunning() const noexcept { return timerPeriodMs > 0; } | |||
/** Returns the timer's interval. | |||
@returns the timer's interval in milliseconds if it's running, or 0 if it's not. | |||
*/ | |||
int getTimerInterval() const noexcept { return periodMs; } | |||
int getTimerInterval() const noexcept { return timerPeriodMs; } | |||
//============================================================================== | |||
@@ -125,11 +124,10 @@ public: | |||
private: | |||
class TimerThread; | |||
friend class TimerThread; | |||
int countdownMs, periodMs; | |||
Timer* previous; | |||
Timer* next; | |||
int timerCountdownMs, timerPeriodMs; // NB: these member variable names are a little verbose | |||
Timer* previousTimer, *nextTimer; // to reduce risk of name-clashes with user subclasses | |||
Timer& operator= (const Timer&); | |||
Timer& operator= (const Timer&) JUCE_DELETED_FUNCTION; | |||
}; | |||
#endif // JUCE_TIMER_H_INCLUDED |
@@ -136,7 +136,7 @@ namespace ColourHelpers | |||
//============================================================================== | |||
Colour::Colour() noexcept | |||
: argb (0) | |||
: argb (0, 0, 0, 0) | |||
{ | |||
} | |||
@@ -151,11 +151,12 @@ Colour& Colour::operator= (const Colour& other) noexcept | |||
return *this; | |||
} | |||
bool Colour::operator== (const Colour& other) const noexcept { return argb.getARGB() == other.argb.getARGB(); } | |||
bool Colour::operator!= (const Colour& other) const noexcept { return argb.getARGB() != other.argb.getARGB(); } | |||
bool Colour::operator== (const Colour& other) const noexcept { return argb.getNativeARGB() == other.argb.getNativeARGB(); } | |||
bool Colour::operator!= (const Colour& other) const noexcept { return argb.getNativeARGB() != other.argb.getNativeARGB(); } | |||
//============================================================================== | |||
Colour::Colour (const uint32 col) noexcept : argb (col) | |||
Colour::Colour (const uint32 col) noexcept | |||
: argb ((col >> 24) & 0xff, (col >> 16) & 0xff, (col >> 8) & 0xff, col & 0xff) | |||
{ | |||
} | |||
@@ -206,10 +207,26 @@ Colour::Colour (const float hue, const float saturation, const float brightness, | |||
{ | |||
} | |||
Colour::Colour (PixelARGB argb_) noexcept | |||
: argb (argb_) | |||
{ | |||
} | |||
Colour::Colour (PixelRGB rgb) noexcept | |||
: argb (Colour (rgb.getInARGBMaskOrder()).argb) | |||
{ | |||
} | |||
Colour::Colour (PixelAlpha alpha) noexcept | |||
: argb (Colour (alpha.getInARGBMaskOrder()).argb) | |||
{ | |||
} | |||
Colour::~Colour() noexcept | |||
{ | |||
} | |||
//============================================================================== | |||
const PixelARGB Colour::getPixelARGB() const noexcept | |||
{ | |||
@@ -220,7 +237,7 @@ const PixelARGB Colour::getPixelARGB() const noexcept | |||
uint32 Colour::getARGB() const noexcept | |||
{ | |||
return argb.getARGB(); | |||
return argb.getInARGBMaskOrder(); | |||
} | |||
//============================================================================== | |||
@@ -238,7 +255,7 @@ Colour Colour::withAlpha (const uint8 newAlpha) const noexcept | |||
{ | |||
PixelARGB newCol (argb); | |||
newCol.setAlpha (newAlpha); | |||
return Colour (newCol.getARGB()); | |||
return Colour (newCol); | |||
} | |||
Colour Colour::withAlpha (const float newAlpha) const noexcept | |||
@@ -247,7 +264,7 @@ Colour Colour::withAlpha (const float newAlpha) const noexcept | |||
PixelARGB newCol (argb); | |||
newCol.setAlpha (ColourHelpers::floatToUInt8 (newAlpha)); | |||
return Colour (newCol.getARGB()); | |||
return Colour (newCol); | |||
} | |||
Colour Colour::withMultipliedAlpha (const float alphaMultiplier) const noexcept | |||
@@ -256,7 +273,7 @@ Colour Colour::withMultipliedAlpha (const float alphaMultiplier) const noexcept | |||
PixelARGB newCol (argb); | |||
newCol.setAlpha ((uint8) jmin (0xff, roundToInt (alphaMultiplier * newCol.getAlpha()))); | |||
return Colour (newCol.getARGB()); | |||
return Colour (newCol); | |||
} | |||
//============================================================================== | |||
@@ -294,7 +311,7 @@ Colour Colour::interpolatedWith (Colour other, float proportionOfOther) const no | |||
c1.tween (c2, (uint32) roundToInt (proportionOfOther * 255.0f)); | |||
c1.unpremultiply(); | |||
return Colour (c1.getARGB()); | |||
return Colour (c1); | |||
} | |||
//============================================================================== | |||
@@ -428,7 +445,7 @@ Colour Colour::contrasting (Colour colour1, | |||
//============================================================================== | |||
String Colour::toString() const | |||
{ | |||
return String::toHexString ((int) argb.getARGB()); | |||
return String::toHexString ((int) argb.getInARGBMaskOrder()); | |||
} | |||
Colour Colour::fromString (StringRef encodedColourString) | |||
@@ -438,7 +455,7 @@ Colour Colour::fromString (StringRef encodedColourString) | |||
String Colour::toDisplayString (const bool includeAlphaValue) const | |||
{ | |||
return String::toHexString ((int) (argb.getARGB() & (includeAlphaValue ? 0xffffffff : 0xffffff))) | |||
return String::toHexString ((int) (argb.getInARGBMaskOrder() & (includeAlphaValue ? 0xffffffff : 0xffffff))) | |||
.paddedLeft ('0', includeAlphaValue ? 8 : 6) | |||
.toUpperCase(); | |||
} |
@@ -115,6 +115,19 @@ public: | |||
float brightness, | |||
float alpha) noexcept; | |||
/** Creates a colour using a PixelARGB object. This function assumes that the argb pixel is | |||
not premultiplied. | |||
*/ | |||
Colour (PixelARGB argb) noexcept; | |||
/** Creates a colour using a PixelRGB object. | |||
*/ | |||
Colour (PixelRGB rgb) noexcept; | |||
/** Creates a colour using a PixelAlpha object. | |||
*/ | |||
Colour (PixelAlpha alpha) noexcept; | |||
/** Creates a colour using floating point hue, saturation and brightness values, and an 8-bit alpha. | |||
The floating point values must be between 0.0 and 1.0. | |||
@@ -46,7 +46,7 @@ inline uint32 clampPixelComponents (uint32 x) noexcept | |||
//============================================================================== | |||
/** | |||
Represents a 32-bit ARGB pixel with premultiplied alpha, and can perform compositing | |||
Represents a 32-bit INTERNAL pixel with premultiplied alpha, and can perform compositing | |||
operations with it. | |||
This is used internally by the imaging classes. | |||
@@ -60,13 +60,6 @@ public: | |||
PixelARGB() noexcept {} | |||
~PixelARGB() noexcept {} | |||
/** Creates a pixel from a 32-bit argb value. | |||
*/ | |||
PixelARGB (const uint32 argbValue) noexcept | |||
: argb (argbValue) | |||
{ | |||
} | |||
PixelARGB (const uint8 a, const uint8 r, const uint8 g, const uint8 b) noexcept | |||
{ | |||
components.b = b; | |||
@@ -75,30 +68,81 @@ public: | |||
components.a = a; | |||
} | |||
forcedinline uint32 getARGB() const noexcept { return argb; } | |||
forcedinline uint32 getUnpremultipliedARGB() const noexcept { PixelARGB p (argb); p.unpremultiply(); return p.getARGB(); } | |||
//============================================================================== | |||
/** Returns a uint32 which represents the pixel in a platform dependent format. */ | |||
forcedinline uint32 getNativeARGB() const noexcept { return internal; } | |||
/** Returns a uint32 which will be in argb order as if constructed with the following mask operation | |||
((alpha << 24) | (red << 16) | (green << 8) | blue). */ | |||
forcedinline uint32 getInARGBMaskOrder() const noexcept | |||
{ | |||
#if JUCE_ANDROID | |||
return (uint32) ((components.a << 24) | (components.r << 16) | (components.g << 8) | (components.b << 0)); | |||
#else | |||
return getNativeARGB(); | |||
#endif | |||
} | |||
/** Returns a uint32 which when written to memory, will be in the order a, r, g, b. In other words, | |||
if the return-value is read as a uint8 array then the elements will be in the order of a, r, g, b*/ | |||
inline uint32 getInARGBMemoryOrder() const noexcept | |||
{ | |||
#if JUCE_BIG_ENDIAN | |||
return getInARGBMaskOrder(); | |||
#else | |||
return (uint32) ((components.b << 24) | (components.g << 16) | (components.r << 8) | components.a); | |||
#endif | |||
} | |||
/** Return channels with an even index and insert zero bytes between them. This is useful for blending | |||
operations. The exact channels which are returned is platform dependent. */ | |||
forcedinline uint32 getEvenBytes() const noexcept { return 0x00ff00ff & internal; } | |||
forcedinline uint32 getRB() const noexcept { return 0x00ff00ff & argb; } | |||
forcedinline uint32 getAG() const noexcept { return 0x00ff00ff & (argb >> 8); } | |||
/** Return channels with an odd index and insert zero bytes between them. This is useful for blending | |||
operations. The exact channels which are returned is platform dependent. */ | |||
forcedinline uint32 getOddBytes() const noexcept { return 0x00ff00ff & (internal >> 8); } | |||
forcedinline uint8 getAlpha() const noexcept { return components.a; } | |||
forcedinline uint8 getRed() const noexcept { return components.r; } | |||
forcedinline uint8 getGreen() const noexcept { return components.g; } | |||
forcedinline uint8 getBlue() const noexcept { return components.b; } | |||
//============================================================================== | |||
forcedinline uint8 getAlpha() const noexcept { return components.a; } | |||
forcedinline uint8 getRed() const noexcept { return components.r; } | |||
forcedinline uint8 getGreen() const noexcept { return components.g; } | |||
forcedinline uint8 getBlue() const noexcept { return components.b; } | |||
#if JUCE_GCC && ! JUCE_CLANG | |||
// NB these are here as a workaround because GCC refuses to bind to packed values. | |||
forcedinline uint8& getAlpha() noexcept { return comps [indexA]; } | |||
forcedinline uint8& getRed() noexcept { return comps [indexR]; } | |||
forcedinline uint8& getGreen() noexcept { return comps [indexG]; } | |||
forcedinline uint8& getBlue() noexcept { return comps [indexB]; } | |||
forcedinline uint8& getAlpha() noexcept { return comps [indexA]; } | |||
forcedinline uint8& getRed() noexcept { return comps [indexR]; } | |||
forcedinline uint8& getGreen() noexcept { return comps [indexG]; } | |||
forcedinline uint8& getBlue() noexcept { return comps [indexB]; } | |||
#else | |||
forcedinline uint8& getAlpha() noexcept { return components.a; } | |||
forcedinline uint8& getRed() noexcept { return components.r; } | |||
forcedinline uint8& getGreen() noexcept { return components.g; } | |||
forcedinline uint8& getBlue() noexcept { return components.b; } | |||
forcedinline uint8& getAlpha() noexcept { return components.a; } | |||
forcedinline uint8& getRed() noexcept { return components.r; } | |||
forcedinline uint8& getGreen() noexcept { return components.g; } | |||
forcedinline uint8& getBlue() noexcept { return components.b; } | |||
#endif | |||
//============================================================================== | |||
/** Copies another pixel colour over this one. | |||
This doesn't blend it - this colour is simply replaced by the other one. | |||
*/ | |||
template <class Pixel> | |||
forcedinline void set (const Pixel& src) noexcept | |||
{ | |||
internal = src.getNativeARGB(); | |||
} | |||
//============================================================================== | |||
/** Sets the pixel's colour from individual components. */ | |||
void setARGB (const uint8 a, const uint8 r, const uint8 g, const uint8 b) noexcept | |||
{ | |||
components.b = b; | |||
components.g = g; | |||
components.r = r; | |||
components.a = a; | |||
} | |||
//============================================================================== | |||
/** Blends another pixel onto this one. | |||
This takes into account the opacity of the pixel being overlaid, and blends | |||
@@ -107,10 +151,15 @@ public: | |||
template <class Pixel> | |||
forcedinline void blend (const Pixel& src) noexcept | |||
{ | |||
const uint32 alpha = 0x100 - src.getAlpha(); | |||
uint32 rb = src.getRB() + maskPixelComponents (getRB() * alpha); | |||
uint32 ag = src.getAG() + maskPixelComponents (getAG() * alpha); | |||
argb = clampPixelComponents (rb) + (clampPixelComponents (ag) << 8); | |||
uint32 rb = src.getEvenBytes(); | |||
uint32 ag = src.getOddBytes(); | |||
const uint32 alpha = 0x100 - (ag >> 16); | |||
rb += maskPixelComponents (getEvenBytes() * alpha); | |||
ag += maskPixelComponents (getOddBytes() * alpha); | |||
internal = clampPixelComponents (rb) | (clampPixelComponents (ag) << 8); | |||
} | |||
/** Blends another pixel onto this one. | |||
@@ -129,14 +178,15 @@ public: | |||
template <class Pixel> | |||
forcedinline void blend (const Pixel& src, uint32 extraAlpha) noexcept | |||
{ | |||
uint32 ag = maskPixelComponents (extraAlpha * src.getAG()); | |||
uint32 rb = maskPixelComponents (extraAlpha * src.getEvenBytes()); | |||
uint32 ag = maskPixelComponents (extraAlpha * src.getOddBytes()); | |||
const uint32 alpha = 0x100 - (ag >> 16); | |||
ag += maskPixelComponents (getAG() * alpha); | |||
uint32 rb = maskPixelComponents (extraAlpha * src.getRB()) | |||
+ maskPixelComponents (getRB() * alpha); | |||
rb += maskPixelComponents (getEvenBytes() * alpha); | |||
ag += maskPixelComponents (getOddBytes() * alpha); | |||
argb = clampPixelComponents(rb) + (clampPixelComponents (ag) << 8); | |||
internal = clampPixelComponents (rb) | (clampPixelComponents (ag) << 8); | |||
} | |||
/** Blends another pixel with this one, creating a colour that is somewhere | |||
@@ -145,29 +195,20 @@ public: | |||
template <class Pixel> | |||
forcedinline void tween (const Pixel& src, const uint32 amount) noexcept | |||
{ | |||
uint32 drb = getRB(); | |||
drb += (((src.getRB() - drb) * amount) >> 8); | |||
drb &= 0x00ff00ff; | |||
uint32 dag = getAG(); | |||
dag += (((src.getAG() - dag) * amount) >> 8); | |||
dag &= 0x00ff00ff; | |||
dag <<= 8; | |||
uint32 dEvenBytes = getEvenBytes(); | |||
dEvenBytes += (((src.getEvenBytes() - dEvenBytes) * amount) >> 8); | |||
dEvenBytes &= 0x00ff00ff; | |||
dag |= drb; | |||
argb = dag; | |||
} | |||
uint32 dOddBytes = getOddBytes(); | |||
dOddBytes += (((src.getOddBytes() - dOddBytes) * amount) >> 8); | |||
dOddBytes &= 0x00ff00ff; | |||
dOddBytes <<= 8; | |||
/** Copies another pixel colour over this one. | |||
This doesn't blend it - this colour is simply replaced by the other one. | |||
*/ | |||
template <class Pixel> | |||
forcedinline void set (const Pixel& src) noexcept | |||
{ | |||
argb = src.getARGB(); | |||
dOddBytes |= dEvenBytes; | |||
internal = dOddBytes; | |||
} | |||
//============================================================================== | |||
/** Replaces the colour's alpha value with another one. */ | |||
forcedinline void setAlpha (const uint8 newAlpha) noexcept | |||
{ | |||
@@ -177,10 +218,12 @@ public: | |||
/** Multiplies the colour's alpha value with another one. */ | |||
forcedinline void multiplyAlpha (int multiplier) noexcept | |||
{ | |||
// increment alpha by 1, so that if multiplier == 255 (full alpha), | |||
// this function will not change the values. | |||
++multiplier; | |||
argb = ((((uint32) multiplier) * getAG()) & 0xff00ff00) | |||
| (((((uint32) multiplier) * getRB()) >> 8) & 0x00ff00ff); | |||
internal = ((((uint32) multiplier) * getOddBytes()) & 0xff00ff00) | |||
| (((((uint32) multiplier) * getEvenBytes()) >> 8) & 0x00ff00ff); | |||
} | |||
forcedinline void multiplyAlpha (const float multiplier) noexcept | |||
@@ -188,14 +231,8 @@ public: | |||
multiplyAlpha ((int) (multiplier * 255.0f)); | |||
} | |||
/** Sets the pixel's colour from individual components. */ | |||
void setARGB (const uint8 a, const uint8 r, const uint8 g, const uint8 b) noexcept | |||
{ | |||
components.b = b; | |||
components.g = g; | |||
components.r = r; | |||
components.a = a; | |||
} | |||
inline PixelARGB getUnpremultiplied() const noexcept { PixelARGB p (internal); p.unpremultiply(); return p; } | |||
/** Premultiplies the pixel's RGB values by its alpha. */ | |||
forcedinline void premultiply() noexcept | |||
@@ -234,9 +271,9 @@ public: | |||
} | |||
else | |||
{ | |||
components.b = (uint8) jmin ((uint32) 0xff, (components.b * 0xff) / alpha); | |||
components.g = (uint8) jmin ((uint32) 0xff, (components.g * 0xff) / alpha); | |||
components.r = (uint8) jmin ((uint32) 0xff, (components.r * 0xff) / alpha); | |||
components.b = (uint8) jmin ((uint32) 0xffu, (components.b * 0xffu) / alpha); | |||
components.g = (uint8) jmin ((uint32) 0xffu, (components.g * 0xffu) / alpha); | |||
components.r = (uint8) jmin ((uint32) 0xffu, (components.r * 0xffu) / alpha); | |||
} | |||
} | |||
} | |||
@@ -257,41 +294,53 @@ public: | |||
} | |||
} | |||
/** Returns a uint32 which when written to memory, will be in the order r, g, b, a. */ | |||
inline uint32 getInRGBAMemoryOrder() const noexcept | |||
{ | |||
#if JUCE_BIG_ENDIAN | |||
return (((uint32) components.r) << 24) | (((uint32) components.g) << 16) | (((uint32) components.b) << 8) | components.a; | |||
#else | |||
return (((uint32) components.a) << 24) | (((uint32) components.b) << 16) | (((uint32) components.g) << 8) | components.r; | |||
#endif | |||
} | |||
//============================================================================== | |||
/** The indexes of the different components in the byte layout of this type of colour. */ | |||
#if JUCE_ANDROID | |||
#if JUCE_BIG_ENDIAN | |||
enum { indexA = 0, indexR = 3, indexG = 2, indexB = 1 }; | |||
#else | |||
enum { indexA = 3, indexR = 0, indexG = 1, indexB = 2 }; | |||
#endif | |||
#else | |||
#if JUCE_BIG_ENDIAN | |||
enum { indexA = 0, indexR = 1, indexG = 2, indexB = 3 }; | |||
#else | |||
enum { indexA = 3, indexR = 2, indexG = 1, indexB = 0 }; | |||
#endif | |||
#endif | |||
private: | |||
//============================================================================== | |||
PixelARGB (const uint32 internalValue) noexcept | |||
: internal (internalValue) | |||
{ | |||
} | |||
//============================================================================== | |||
struct Components | |||
{ | |||
#if JUCE_ANDROID | |||
#if JUCE_BIG_ENDIAN | |||
uint8 a, b, g, r; | |||
#else | |||
uint8 r, g, b, a; | |||
#endif | |||
#else | |||
#if JUCE_BIG_ENDIAN | |||
uint8 a, r, g, b; | |||
#else | |||
uint8 b, g, r, a; | |||
#endif | |||
#endif | |||
} JUCE_PACKED; | |||
union | |||
{ | |||
uint32 argb; | |||
uint32 internal; | |||
Components components; | |||
#if JUCE_GCC | |||
uint8 comps[4]; | |||
uint8 comps[4]; // helper struct needed because gcc does not allow references to packed union members | |||
#endif | |||
}; | |||
} | |||
@@ -316,23 +365,64 @@ public: | |||
PixelRGB() noexcept {} | |||
~PixelRGB() noexcept {} | |||
/** Creates a pixel from a 32-bit argb value. | |||
//============================================================================== | |||
/** Returns a uint32 which represents the pixel in a platform dependent format which is compatible | |||
with the native format of a PixelARGB. | |||
(The argb format is that used by PixelARGB) | |||
*/ | |||
PixelRGB (const uint32 argb) noexcept | |||
@see PixelARGB::getNativeARGB */ | |||
forcedinline uint32 getNativeARGB() const noexcept | |||
{ | |||
#if JUCE_ANDROID | |||
return (uint32) ((0xff << 24) | r | (g << 8) | (b << 16)); | |||
#else | |||
return (uint32) ((0xff << 24) | b | (g << 8) | (r << 16)); | |||
#endif | |||
} | |||
/** Returns a uint32 which will be in argb order as if constructed with the following mask operation | |||
((alpha << 24) | (red << 16) | (green << 8) | blue). */ | |||
forcedinline uint32 getInARGBMaskOrder() const noexcept | |||
{ | |||
#if JUCE_ANDROID | |||
return (uint32) ((0xff << 24) | (r << 16) | (g << 8) | (b << 0)); | |||
#else | |||
return getNativeARGB(); | |||
#endif | |||
} | |||
/** Returns a uint32 which when written to memory, will be in the order a, r, g, b. In other words, | |||
if the return-value is read as a uint8 array then the elements will be in the order of a, r, g, b*/ | |||
inline uint32 getInARGBMemoryOrder() const noexcept | |||
{ | |||
r = (uint8) (argb >> 16); | |||
g = (uint8) (argb >> 8); | |||
b = (uint8) (argb); | |||
#if JUCE_BIG_ENDIAN | |||
return getInARGBMaskOrder(); | |||
#else | |||
return (uint32) ((b << 24) | (g << 16) | (r << 8) | 0xff); | |||
#endif | |||
} | |||
forcedinline uint32 getARGB() const noexcept { return 0xff000000 | b | (((uint32) g) << 8) | (((uint32) r) << 16); } | |||
forcedinline uint32 getUnpremultipliedARGB() const noexcept { return getARGB(); } | |||
/** Return channels with an even index and insert zero bytes between them. This is useful for blending | |||
operations. The exact channels which are returned is platform dependent but compatible with the | |||
return value of getEvenBytes of the PixelARGB class. | |||
forcedinline uint32 getRB() const noexcept { return b | (uint32) (r << 16); } | |||
forcedinline uint32 getAG() const noexcept { return (uint32) (0xff0000 | g); } | |||
@see PixelARGB::getEvenBytes */ | |||
forcedinline uint32 getEvenBytes() const noexcept | |||
{ | |||
#if JUCE_ANDROID | |||
return (uint32) (r | (b << 16)); | |||
#else | |||
return (uint32) (b | (r << 16)); | |||
#endif | |||
} | |||
/** Return channels with an odd index and insert zero bytes between them. This is useful for blending | |||
operations. The exact channels which are returned is platform dependent but compatible with the | |||
return value of getOddBytes of the PixelARGB class. | |||
@see PixelARGB::getOddBytes */ | |||
forcedinline uint32 getOddBytes() const noexcept { return (uint32)0xff0000 | g; } | |||
//============================================================================== | |||
forcedinline uint8 getAlpha() const noexcept { return 0xff; } | |||
forcedinline uint8 getRed() const noexcept { return r; } | |||
forcedinline uint8 getGreen() const noexcept { return g; } | |||
@@ -342,6 +432,30 @@ public: | |||
forcedinline uint8& getGreen() noexcept { return g; } | |||
forcedinline uint8& getBlue() noexcept { return b; } | |||
//============================================================================== | |||
/** Copies another pixel colour over this one. | |||
This doesn't blend it - this colour is simply replaced by the other one. | |||
Because PixelRGB has no alpha channel, any alpha value in the source pixel | |||
is thrown away. | |||
*/ | |||
template <class Pixel> | |||
forcedinline void set (const Pixel& src) noexcept | |||
{ | |||
b = src.getBlue(); | |||
g = src.getGreen(); | |||
r = src.getRed(); | |||
} | |||
/** Sets the pixel's colour from individual components. */ | |||
void setARGB (const uint8, const uint8 red, const uint8 green, const uint8 blue) noexcept | |||
{ | |||
r = red; | |||
g = green; | |||
b = blue; | |||
} | |||
//============================================================================== | |||
/** Blends another pixel onto this one. | |||
This takes into account the opacity of the pixel being overlaid, and blends | |||
@@ -350,14 +464,22 @@ public: | |||
template <class Pixel> | |||
forcedinline void blend (const Pixel& src) noexcept | |||
{ | |||
const uint32 alpha = 0x100 - src.getAlpha(); | |||
const uint32 alpha = (uint32) (0x100 - src.getAlpha()); | |||
// getEvenBytes returns 0x00rr00bb on non-android | |||
uint32 rb = clampPixelComponents (src.getEvenBytes() + maskPixelComponents (getEvenBytes() * alpha)); | |||
// getOddBytes returns 0x00aa00gg on non-android | |||
uint32 ag = clampPixelComponents (src.getOddBytes() + ((g * alpha) >> 8)); | |||
uint32 rb = clampPixelComponents (src.getRB() + maskPixelComponents (getRB() * alpha)); | |||
uint32 ag = src.getAG() + (g * alpha >> 8); | |||
g = (uint8) (ag & 0xff); | |||
#if JUCE_ANDROID | |||
b = (uint8) (rb >> 16); | |||
r = (uint8) (rb & 0xff); | |||
#else | |||
r = (uint8) (rb >> 16); | |||
g = (uint8) clampPixelComponents (ag); | |||
b = (uint8) rb; | |||
b = (uint8) (rb & 0xff); | |||
#endif | |||
} | |||
forcedinline void blend (const PixelRGB src) noexcept | |||
@@ -373,16 +495,23 @@ public: | |||
template <class Pixel> | |||
forcedinline void blend (const Pixel& src, uint32 extraAlpha) noexcept | |||
{ | |||
uint32 ag = maskPixelComponents (extraAlpha * src.getAG()); | |||
uint32 ag = maskPixelComponents (extraAlpha * src.getOddBytes()); | |||
uint32 rb = maskPixelComponents (extraAlpha * src.getEvenBytes()); | |||
const uint32 alpha = 0x100 - (ag >> 16); | |||
ag += g * alpha >> 8; | |||
uint32 rb = clampPixelComponents (maskPixelComponents (extraAlpha * src.getRB()) | |||
+ maskPixelComponents (getRB() * alpha)); | |||
ag = clampPixelComponents (ag + (g * alpha >> 8)); | |||
rb = clampPixelComponents (rb + maskPixelComponents (getEvenBytes() * alpha)); | |||
g = (uint8) (ag & 0xff); | |||
b = (uint8) rb; | |||
g = (uint8) clampPixelComponents (ag); | |||
#if JUCE_ANDROID | |||
b = (uint8) (rb >> 16); | |||
r = (uint8) (rb & 0xff); | |||
#else | |||
r = (uint8) (rb >> 16); | |||
b = (uint8) (rb & 0xff); | |||
#endif | |||
} | |||
/** Blends another pixel with this one, creating a colour that is somewhere | |||
@@ -391,31 +520,24 @@ public: | |||
template <class Pixel> | |||
forcedinline void tween (const Pixel& src, const uint32 amount) noexcept | |||
{ | |||
uint32 drb = getRB(); | |||
drb += (((src.getRB() - drb) * amount) >> 8); | |||
uint32 dEvenBytes = getEvenBytes(); | |||
dEvenBytes += (((src.getEvenBytes() - dEvenBytes) * amount) >> 8); | |||
uint32 dag = getAG(); | |||
dag += (((src.getAG() - dag) * amount) >> 8); | |||
uint32 dOddBytes = getOddBytes(); | |||
dOddBytes += (((src.getOddBytes() - dOddBytes) * amount) >> 8); | |||
b = (uint8) drb; | |||
g = (uint8) dag; | |||
r = (uint8) (drb >> 16); | |||
} | |||
g = (uint8) (dOddBytes & 0xff); // dOddBytes = 0x00aa00gg | |||
/** Copies another pixel colour over this one. | |||
This doesn't blend it - this colour is simply replaced by the other one. | |||
Because PixelRGB has no alpha channel, any alpha value in the source pixel | |||
is thrown away. | |||
*/ | |||
template <class Pixel> | |||
forcedinline void set (const Pixel& src) noexcept | |||
{ | |||
b = src.getBlue(); | |||
g = src.getGreen(); | |||
r = src.getRed(); | |||
#if JUCE_ANDROID | |||
r = (uint8) (dEvenBytes & 0xff); // dEvenBytes = 0x00bb00rr | |||
b = (uint8) (dEvenBytes >> 16); | |||
#else | |||
b = (uint8) (dEvenBytes & 0xff); // dEvenBytes = 0x00rr00bb | |||
r = (uint8) (dEvenBytes >> 16); | |||
#endif | |||
} | |||
//============================================================================== | |||
/** This method is included for compatibility with the PixelARGB class. */ | |||
forcedinline void setAlpha (const uint8) noexcept {} | |||
@@ -425,14 +547,6 @@ public: | |||
/** Multiplies the colour's alpha value with another one. */ | |||
forcedinline void multiplyAlpha (float) noexcept {} | |||
/** Sets the pixel's colour from individual components. */ | |||
void setARGB (const uint8, const uint8 red, const uint8 green, const uint8 blue) noexcept | |||
{ | |||
r = red; | |||
g = green; | |||
b = blue; | |||
} | |||
/** Premultiplies the pixel's RGB values by its alpha. */ | |||
forcedinline void premultiply() noexcept {} | |||
@@ -454,6 +568,20 @@ public: | |||
private: | |||
//============================================================================== | |||
PixelRGB (const uint32 internal) noexcept | |||
{ | |||
#if JUCE_ANDROID | |||
b = (uint8) (internal >> 16); | |||
g = (uint8) (internal >> 8); | |||
r = (uint8) (internal); | |||
#else | |||
r = (uint8) (internal >> 16); | |||
g = (uint8) (internal >> 8); | |||
b = (uint8) (internal); | |||
#endif | |||
} | |||
//============================================================================== | |||
#if JUCE_MAC | |||
uint8 r, g, b; | |||
#else | |||
@@ -486,21 +614,36 @@ public: | |||
PixelAlpha() noexcept {} | |||
~PixelAlpha() noexcept {} | |||
/** Creates a pixel from a 32-bit argb value. | |||
//============================================================================== | |||
/** Returns a uint32 which represents the pixel in a platform dependent format which is compatible | |||
with the native format of a PixelARGB. | |||
(The argb format is that used by PixelARGB) | |||
*/ | |||
PixelAlpha (const uint32 argb) noexcept | |||
{ | |||
a = (uint8) (argb >> 24); | |||
} | |||
@see PixelARGB::getNativeARGB */ | |||
forcedinline uint32 getNativeARGB() const noexcept { return (uint32) ((a << 24) | (a << 16) | (a << 8) | a); } | |||
forcedinline uint32 getARGB() const noexcept { return (((uint32) a) << 24) | (((uint32) a) << 16) | (((uint32) a) << 8) | a; } | |||
forcedinline uint32 getUnpremultipliedARGB() const noexcept { return (((uint32) a) << 24) | 0xffffff; } | |||
/** Returns a uint32 which will be in argb order as if constructed with the following mask operation | |||
((alpha << 24) | (red << 16) | (green << 8) | blue). */ | |||
forcedinline uint32 getInARGBMaskOrder() const noexcept { return getNativeARGB(); } | |||
forcedinline uint32 getRB() const noexcept { return (((uint32) a) << 16) | a; } | |||
forcedinline uint32 getAG() const noexcept { return (((uint32) a) << 16) | a; } | |||
/** Returns a uint32 which when written to memory, will be in the order a, r, g, b. In other words, | |||
if the return-value is read as a uint8 array then the elements will be in the order of a, r, g, b*/ | |||
inline uint32 getInARGBMemoryOrder() const noexcept { return getNativeARGB(); } | |||
/** Return channels with an even index and insert zero bytes between them. This is useful for blending | |||
operations. The exact channels which are returned is platform dependent but compatible with the | |||
return value of getEvenBytes of the PixelARGB class. | |||
@see PixelARGB::getEvenBytes */ | |||
forcedinline uint32 getEvenBytes() const noexcept { return (uint32) ((a << 16) | a); } | |||
/** Return channels with an odd index and insert zero bytes between them. This is useful for blending | |||
operations. The exact channels which are returned is platform dependent but compatible with the | |||
return value of getOddBytes of the PixelARGB class. | |||
@see PixelARGB::getOddBytes */ | |||
forcedinline uint32 getOddBytes() const noexcept { return (uint32) ((a << 16) | a); } | |||
//============================================================================== | |||
forcedinline uint8 getAlpha() const noexcept { return a; } | |||
forcedinline uint8& getAlpha() noexcept { return a; } | |||
@@ -508,6 +651,24 @@ public: | |||
forcedinline uint8 getGreen() const noexcept { return 0; } | |||
forcedinline uint8 getBlue() const noexcept { return 0; } | |||
//============================================================================== | |||
/** Copies another pixel colour over this one. | |||
This doesn't blend it - this colour is simply replaced by the other one. | |||
*/ | |||
template <class Pixel> | |||
forcedinline void set (const Pixel& src) noexcept | |||
{ | |||
a = src.getAlpha(); | |||
} | |||
/** Sets the pixel's colour from individual components. */ | |||
forcedinline void setARGB (const uint8 a_, const uint8 /*r*/, const uint8 /*g*/, const uint8 /*b*/) noexcept | |||
{ | |||
a = a_; | |||
} | |||
//============================================================================== | |||
/** Blends another pixel onto this one. | |||
This takes into account the opacity of the pixel being overlaid, and blends | |||
@@ -542,16 +703,7 @@ public: | |||
a += ((src.getAlpha() - a) * amount) >> 8; | |||
} | |||
/** Copies another pixel colour over this one. | |||
This doesn't blend it - this colour is simply replaced by the other one. | |||
*/ | |||
template <class Pixel> | |||
forcedinline void set (const Pixel& src) noexcept | |||
{ | |||
a = src.getAlpha(); | |||
} | |||
//============================================================================== | |||
/** Replaces the colour's alpha value with another one. */ | |||
forcedinline void setAlpha (const uint8 newAlpha) noexcept | |||
{ | |||
@@ -570,12 +722,6 @@ public: | |||
a = (uint8) (a * multiplier); | |||
} | |||
/** Sets the pixel's colour from individual components. */ | |||
forcedinline void setARGB (const uint8 a_, const uint8 /*r*/, const uint8 /*g*/, const uint8 /*b*/) noexcept | |||
{ | |||
a = a_; | |||
} | |||
/** Premultiplies the pixel's RGB values by its alpha. */ | |||
forcedinline void premultiply() noexcept {} | |||
@@ -589,6 +735,12 @@ public: | |||
enum { indexA = 0 }; | |||
private: | |||
//============================================================================== | |||
PixelAlpha (const uint32 internal) noexcept | |||
{ | |||
a = (uint8) (internal >> 24); | |||
} | |||
//============================================================================== | |||
uint8 a; | |||
} | |||
@@ -203,7 +203,7 @@ public: | |||
The minimumHorizontalScale parameter specifies how much the text can be squashed horizontally | |||
to try to squeeze it into the space. If you don't want any horizontal scaling to occur, you | |||
can set this value to 1.0f. | |||
can set this value to 1.0f. Pass 0 if you want it to use a default value. | |||
@see GlyphArrangement::addFittedText | |||
*/ | |||
@@ -211,7 +211,7 @@ public: | |||
int x, int y, int width, int height, | |||
Justification justificationFlags, | |||
int maximumNumberOfLines, | |||
float minimumHorizontalScale = 0.7f) const; | |||
float minimumHorizontalScale = 0.0f) const; | |||
/** Tries to draw a text string inside a given space. | |||
@@ -228,7 +228,7 @@ public: | |||
The minimumHorizontalScale parameter specifies how much the text can be squashed horizontally | |||
to try to squeeze it into the space. If you don't want any horizontal scaling to occur, you | |||
can set this value to 1.0f. | |||
can set this value to 1.0f. Pass 0 if you want it to use a default value. | |||
@see GlyphArrangement::addFittedText | |||
*/ | |||
@@ -236,7 +236,7 @@ public: | |||
const Rectangle<int>& area, | |||
Justification justificationFlags, | |||
int maximumNumberOfLines, | |||
float minimumHorizontalScale = 0.7f) const; | |||
float minimumHorizontalScale = 0.0f) const; | |||
//============================================================================== | |||
/** Fills the context's entire clip region with the current colour or brush. | |||
@@ -374,21 +374,29 @@ public: | |||
//============================================================================== | |||
/** Draws a line between two points. | |||
The line is 1 pixel wide and drawn with the current colour or brush. | |||
TIP: If you're trying to draw horizontal or vertical lines, don't use this - | |||
it's better to use fillRect() instead unless you really need an angled line. | |||
*/ | |||
void drawLine (float startX, float startY, float endX, float endY) const; | |||
/** Draws a line between two points with a given thickness. | |||
TIP: If you're trying to draw horizontal or vertical lines, don't use this - | |||
it's better to use fillRect() instead unless you really need an angled line. | |||
@see Path::addLineSegment | |||
*/ | |||
void drawLine (float startX, float startY, float endX, float endY, float lineThickness) const; | |||
/** Draws a line between two points. | |||
The line is 1 pixel wide and drawn with the current colour or brush. | |||
TIP: If you're trying to draw horizontal or vertical lines, don't use this - | |||
it's better to use fillRect() instead unless you really need an angled line. | |||
*/ | |||
void drawLine (const Line<float>& line) const; | |||
/** Draws a line between two points with a given thickness. | |||
@see Path::addLineSegment | |||
TIP: If you're trying to draw horizontal or vertical lines, don't use this - | |||
it's better to use fillRect() instead unless you really need an angled line. | |||
*/ | |||
void drawLine (const Line<float>& line, float lineThickness) const; | |||
@@ -433,11 +433,11 @@ void LowLevelGraphicsPostScriptRenderer::writeImage (const Image& im, | |||
{ | |||
PixelARGB p (*(const PixelARGB*) pixelData); | |||
p.unpremultiply(); | |||
pixel = Colours::white.overlaidWith (Colour (p.getARGB())); | |||
pixel = Colours::white.overlaidWith (Colour (p)); | |||
} | |||
else if (im.isRGB()) | |||
{ | |||
pixel = Colour (((const PixelRGB*) pixelData)->getARGB()); | |||
pixel = Colour (*((const PixelRGB*) pixelData)); | |||
} | |||
else | |||
{ | |||
@@ -30,6 +30,7 @@ namespace FontValues | |||
} | |||
const float defaultFontHeight = 14.0f; | |||
float minimumHorizontalScale = 0.7f; | |||
String fallbackFont; | |||
String fallbackFontStyle; | |||
} | |||
@@ -37,6 +38,9 @@ namespace FontValues | |||
typedef Typeface::Ptr (*GetTypefaceForFont) (const Font&); | |||
GetTypefaceForFont juce_getTypefaceForFont = nullptr; | |||
float Font::getDefaultMinimumHorizontalScaleFactor() noexcept { return FontValues::minimumHorizontalScale; } | |||
void Font::setDefaultMinimumHorizontalScaleFactor (float newValue) noexcept { FontValues::minimumHorizontalScale = newValue; } | |||
//============================================================================== | |||
class TypefaceCache : private DeletedAtShutdown | |||
{ | |||
@@ -319,6 +319,18 @@ public: | |||
*/ | |||
void setHorizontalScale (float scaleFactor); | |||
/** Returns the minimum horizontal scale to which fonts may be squashed when trying to | |||
create a layout. | |||
@see setDefaultMinimumHorizontalScaleFactor | |||
*/ | |||
static float getDefaultMinimumHorizontalScaleFactor() noexcept; | |||
/** Sets the minimum horizontal scale to which fonts may be squashed when trying to | |||
create a text layout. | |||
@see getDefaultMinimumHorizontalScaleFactor | |||
*/ | |||
static void setDefaultMinimumHorizontalScaleFactor (float newMinimumScaleFactor) noexcept; | |||
/** Returns the font's kerning. | |||
This is the extra space added between adjacent characters, as a proportion | |||
@@ -366,8 +366,11 @@ void GlyphArrangement::addFittedText (const Font& f, | |||
const float width, const float height, | |||
Justification layout, | |||
int maximumLines, | |||
const float minimumHorizontalScale) | |||
float minimumHorizontalScale) | |||
{ | |||
if (minimumHorizontalScale == 0.0f) | |||
minimumHorizontalScale = Font::getDefaultMinimumHorizontalScaleFactor(); | |||
// doesn't make much sense if this is outside a sensible range of 0.5 to 1.0 | |||
jassert (minimumHorizontalScale > 0 && minimumHorizontalScale <= 1.0f); | |||
@@ -208,6 +208,10 @@ public: | |||
A Justification parameter lets you specify how the text is laid out within the rectangle, | |||
both horizontally and vertically. | |||
The minimumHorizontalScale parameter specifies how much the text can be squashed horizontally | |||
to try to squeeze it into the space. If you don't want any horizontal scaling to occur, you | |||
can set this value to 1.0f. Pass 0 if you want it to use the default value. | |||
@see Graphics::drawFittedText | |||
*/ | |||
void addFittedText (const Font& font, | |||
@@ -215,7 +219,7 @@ public: | |||
float x, float y, float width, float height, | |||
Justification layout, | |||
int maximumLinesToUse, | |||
float minimumHorizontalScale = 0.7f); | |||
float minimumHorizontalScale = 0.0f); | |||
/** Appends another glyph arrangement to this one. */ | |||
void addGlyphArrangement (const GlyphArrangement&); | |||
@@ -635,4 +635,9 @@ void TextLayout::recalculateSize (const AttributedString& text) | |||
width = bounds.getWidth(); | |||
height = bounds.getHeight(); | |||
} | |||
else | |||
{ | |||
width = 0; | |||
height = 0; | |||
} | |||
} |
@@ -224,8 +224,8 @@ public: | |||
if (length <= 0) | |||
return start; | |||
return Point<ValueType> (start.x + static_cast <ValueType> ((delta.x * distanceFromStart - delta.y * perpendicularDistance) / length), | |||
start.y + static_cast <ValueType> ((delta.y * distanceFromStart + delta.x * perpendicularDistance) / length)); | |||
return Point<ValueType> (start.x + static_cast<ValueType> ((delta.x * distanceFromStart - delta.y * perpendicularDistance) / length), | |||
start.y + static_cast<ValueType> ((delta.y * distanceFromStart + delta.x * perpendicularDistance) / length)); | |||
} | |||
/** Returns the location of the point which is a given distance along this line | |||
@@ -267,7 +267,7 @@ public: | |||
if (prop >= 0 && prop <= 1.0) | |||
{ | |||
pointOnLine = start + delta * static_cast <ValueType> (prop); | |||
pointOnLine = start + delta * static_cast<ValueType> (prop); | |||
return targetPoint.getDistanceFrom (pointOnLine); | |||
} | |||
} | |||
@@ -301,9 +301,9 @@ public: | |||
const double length = delta.x * delta.x + delta.y * delta.y; | |||
return length <= 0 ? 0 | |||
: jlimit (ValueType(), static_cast <ValueType> (1), | |||
static_cast <ValueType> ((((point.x - start.x) * delta.x | |||
+ (point.y - start.y) * delta.y) / length))); | |||
: jlimit (ValueType(), static_cast<ValueType> (1), | |||
static_cast<ValueType> ((((point.x - start.x) * delta.x | |||
+ (point.y - start.y) * delta.y) / length))); | |||
} | |||
/** Finds the point on this line which is nearest to a given point. | |||
@@ -375,40 +375,40 @@ private: | |||
{ | |||
const ValueType along = (p1.y - p3.y) / d2.y; | |||
intersection = p1.withX (p3.x + along * d2.x); | |||
return along >= 0 && along <= static_cast <ValueType> (1); | |||
return along >= 0 && along <= static_cast<ValueType> (1); | |||
} | |||
else if (d2.y == 0 && d1.y != 0) | |||
{ | |||
const ValueType along = (p3.y - p1.y) / d1.y; | |||
intersection = p3.withX (p1.x + along * d1.x); | |||
return along >= 0 && along <= static_cast <ValueType> (1); | |||
return along >= 0 && along <= static_cast<ValueType> (1); | |||
} | |||
else if (d1.x == 0 && d2.x != 0) | |||
{ | |||
const ValueType along = (p1.x - p3.x) / d2.x; | |||
intersection = p1.withY (p3.y + along * d2.y); | |||
return along >= 0 && along <= static_cast <ValueType> (1); | |||
return along >= 0 && along <= static_cast<ValueType> (1); | |||
} | |||
else if (d2.x == 0 && d1.x != 0) | |||
{ | |||
const ValueType along = (p3.x - p1.x) / d1.x; | |||
intersection = p3.withY (p1.y + along * d1.y); | |||
return along >= 0 && along <= static_cast <ValueType> (1); | |||
return along >= 0 && along <= static_cast<ValueType> (1); | |||
} | |||
} | |||
intersection = (p2 + p3) / static_cast <ValueType> (2); | |||
intersection = (p2 + p3) / static_cast<ValueType> (2); | |||
return false; | |||
} | |||
const ValueType along1 = ((p1.y - p3.y) * d2.x - (p1.x - p3.x) * d2.y) / divisor; | |||
intersection = p1 + d1 * along1; | |||
if (along1 < 0 || along1 > static_cast <ValueType> (1)) | |||
if (along1 < 0 || along1 > static_cast<ValueType> (1)) | |||
return false; | |||
const ValueType along2 = ((p1.y - p3.y) * d1.x - (p1.x - p3.x) * d1.y) / divisor; | |||
return along2 >= 0 && along2 <= static_cast <ValueType> (1); | |||
return along2 >= 0 && along2 <= static_cast<ValueType> (1); | |||
} | |||
}; | |||
@@ -1567,17 +1567,17 @@ void Path::restoreFromString (StringRef stringVersion) | |||
} | |||
//============================================================================== | |||
Path::Iterator::Iterator (const Path& path_) | |||
: path (path_), | |||
index (0) | |||
Path::Iterator::Iterator (const Path& p) noexcept | |||
: x1 (0), y1 (0), x2 (0), y2 (0), x3 (0), y3 (0), | |||
path (p), index (0) | |||
{ | |||
} | |||
Path::Iterator::~Iterator() | |||
Path::Iterator::~Iterator() noexcept | |||
{ | |||
} | |||
bool Path::Iterator::next() | |||
bool Path::Iterator::next() noexcept | |||
{ | |||
const float* const elements = path.data.elements; | |||
@@ -499,21 +499,20 @@ public: | |||
/** Adds a "pie-chart" shape to the path. | |||
The shape is added as a new sub-path. (Any currently open paths will be | |||
left open). | |||
The shape is added as a new sub-path. (Any currently open paths will be left open). | |||
Note that when specifying the start and end angles, the curve will be drawn either clockwise | |||
or anti-clockwise according to whether the end angle is greater than the start. This means | |||
that sometimes you may need to use values greater than 2*Pi for the end angle. | |||
@param area the outer rectangle in which the elliptical outline fits | |||
@param fromRadians the angle (clockwise) in radians at which to start the arc segment (where 0 is the | |||
top-centre of the ellipse) | |||
@param toRadians the angle (clockwise) in radians at which to end the arc segment (where 0 is the | |||
top-centre of the ellipse) | |||
@param segmentBounds the outer rectangle in which the elliptical outline fits | |||
@param fromRadians the angle (clockwise) in radians at which to start the arc segment (where 0 is the | |||
top-centre of the ellipse) | |||
@param toRadians the angle (clockwise) in radians at which to end the arc segment (where 0 is the | |||
top-centre of the ellipse) | |||
@param innerCircleProportionalSize if this is > 0, then the pie will be drawn as a curved band around a hollow | |||
ellipse at its centre, where this value indicates the inner ellipse's size with | |||
respect to the outer one. | |||
ellipse at its centre, where this value indicates the inner ellipse's size with | |||
respect to the outer one. | |||
@see addArc | |||
*/ | |||
void addPieSegment (Rectangle<float> segmentBounds, | |||
@@ -718,8 +717,8 @@ public: | |||
{ | |||
public: | |||
//============================================================================== | |||
Iterator (const Path& path); | |||
~Iterator(); | |||
Iterator (const Path& path) noexcept; | |||
~Iterator() noexcept; | |||
//============================================================================== | |||
/** Moves onto the next element in the path. | |||
@@ -728,7 +727,7 @@ public: | |||
the elementType variable will be set to the type of the current element, | |||
and some of the x and y variables will be filled in with values. | |||
*/ | |||
bool next(); | |||
bool next() noexcept; | |||
//============================================================================== | |||
enum PathElementType | |||