| @@ -116,8 +116,11 @@ void AudioSampleBuffer::setDataToReferTo (float** dataToReferTo, | |||||
| jassert (dataToReferTo != nullptr); | jassert (dataToReferTo != nullptr); | ||||
| jassert (newNumChannels >= 0 && newNumSamples >= 0); | jassert (newNumChannels >= 0 && newNumSamples >= 0); | ||||
| allocatedBytes = 0; | |||||
| allocatedData.free(); | |||||
| if (allocatedBytes != 0) | |||||
| { | |||||
| allocatedBytes = 0; | |||||
| allocatedData.free(); | |||||
| } | |||||
| numChannels = newNumChannels; | numChannels = newNumChannels; | ||||
| size = newNumSamples; | size = newNumSamples; | ||||
| @@ -49,8 +49,13 @@ namespace FloatVectorHelpers | |||||
| { | { | ||||
| typedef float Type; | typedef float Type; | ||||
| typedef __m128 ParallelType; | typedef __m128 ParallelType; | ||||
| typedef __m128 IntegerType; | |||||
| enum { numParallel = 4 }; | 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 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 loadA (const Type* v) noexcept { return _mm_load_ps (v); } | ||||
| static forcedinline ParallelType loadU (const Type* v) noexcept { return _mm_loadu_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 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 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 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]); } | 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 double Type; | ||||
| typedef __m128d ParallelType; | typedef __m128d ParallelType; | ||||
| typedef __m128d IntegerType; | |||||
| enum { numParallel = 2 }; | 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 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 loadA (const Type* v) noexcept { return _mm_load_pd (v); } | ||||
| static forcedinline ParallelType loadU (const Type* v) noexcept { return _mm_loadu_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 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 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 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]); } | static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1]); } | ||||
| }; | }; | ||||
| #define JUCE_BEGIN_VEC_OP \ | #define JUCE_BEGIN_VEC_OP \ | ||||
| typedef FloatVectorHelpers::ModeType<sizeof(*dest)>::Mode Mode; \ | typedef FloatVectorHelpers::ModeType<sizeof(*dest)>::Mode Mode; \ | ||||
| if (FloatVectorHelpers::isSSE2Available()) \ | if (FloatVectorHelpers::isSSE2Available()) \ | ||||
| @@ -126,23 +148,62 @@ namespace FloatVectorHelpers | |||||
| #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \ | #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \ | ||||
| JUCE_BEGIN_VEC_OP \ | JUCE_BEGIN_VEC_OP \ | ||||
| setupOp \ | 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) | JUCE_FINISH_VEC_OP (normalOp) | ||||
| #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST(normalOp, vecOp, locals, increment, setupOp) \ | #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST(normalOp, vecOp, locals, increment, setupOp) \ | ||||
| JUCE_BEGIN_VEC_OP \ | JUCE_BEGIN_VEC_OP \ | ||||
| setupOp \ | 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) | JUCE_FINISH_VEC_OP (normalOp) | ||||
| @@ -154,8 +215,12 @@ namespace FloatVectorHelpers | |||||
| { | { | ||||
| typedef float Type; | typedef float Type; | ||||
| typedef float32x4_t ParallelType; | typedef float32x4_t ParallelType; | ||||
| typedef uint32x4_t IntegerType; | |||||
| enum { numParallel = 4 }; | 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 load1 (Type v) noexcept { return vld1q_dup_f32 (&v); } | ||||
| static forcedinline ParallelType loadA (const Type* v) noexcept { return vld1q_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); } | 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 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 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 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]); } | 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 Type; | ||||
| typedef double ParallelType; | typedef double ParallelType; | ||||
| typedef uint64 IntegerType; | |||||
| enum { numParallel = 1 }; | 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 load1 (Type v) noexcept { return v; } | ||||
| static forcedinline ParallelType loadA (const 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; } | 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 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 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 max (ParallelType a) noexcept { return a; } | ||||
| static forcedinline Type min (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 | #if JUCE_USE_VDSP_FRAMEWORK | ||||
| vDSP_vclr (dest, 1, (size_t) num); | vDSP_vclr (dest, 1, (size_t) num); | ||||
| #else | #else | ||||
| zeromem (dest, num * sizeof (float)); | |||||
| zeromem (dest, (size_t) num * sizeof (float)); | |||||
| #endif | #endif | ||||
| } | } | ||||
| @@ -437,7 +516,7 @@ void JUCE_CALLTYPE FloatVectorOperations::clear (double* dest, int num) noexcept | |||||
| #if JUCE_USE_VDSP_FRAMEWORK | #if JUCE_USE_VDSP_FRAMEWORK | ||||
| vDSP_vclrD (dest, 1, (size_t) num); | vDSP_vclrD (dest, 1, (size_t) num); | ||||
| #else | #else | ||||
| zeromem (dest, num * sizeof (double)); | |||||
| zeromem (dest, (size_t) num * sizeof (double)); | |||||
| #endif | #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 | 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, | JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, Mode::add (d, amountToAdd), JUCE_LOAD_DEST, | ||||
| const Mode::ParallelType amountToAdd = Mode::load1 (amount);) | const Mode::ParallelType amountToAdd = Mode::load1 (amount);) | ||||
| #endif | |||||
| } | } | ||||
| void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, double amount, int num) noexcept | 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 | 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_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, | JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, | ||||
| const Mode::ParallelType mult = Mode::load1 (multiplier);) | const Mode::ParallelType mult = Mode::load1 (multiplier);) | ||||
| #endif | |||||
| } | } | ||||
| void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept | 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 | #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 | void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, const int* src, float multiplier, int num) noexcept | ||||
| { | { | ||||
| #if JUCE_USE_ARM_NEON | #if JUCE_USE_ARM_NEON | ||||
| @@ -966,6 +1080,12 @@ public: | |||||
| FloatVectorOperations::subtract (data1, data2, num); | FloatVectorOperations::subtract (data1, data2, num); | ||||
| u.expect (areAllValuesEqual (data1, num, (ValueType) 512)); | 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); | fillRandomly (random, int1, num); | ||||
| doConversionTest (u, data1, data2, int1, num); | doConversionTest (u, data1, data2, int1, num); | ||||
| @@ -137,6 +137,12 @@ public: | |||||
| /** Copies a source vector to a destination, negating each value. */ | /** Copies a source vector to a destination, negating each value. */ | ||||
| static void JUCE_CALLTYPE negate (double* dest, const double* src, int numValues) noexcept; | 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. */ | /** 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; | static void JUCE_CALLTYPE convertFixedToFloat (float* dest, const int* src, float multiplier, int numValues) noexcept; | ||||
| @@ -333,7 +333,7 @@ private: | |||||
| if (countdown <= 0) | if (countdown <= 0) | ||||
| currentValue = target; | currentValue = target; | ||||
| else | 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)) | #if __ARM_NEON__ && ! (JUCE_USE_VDSP_FRAMEWORK || defined (JUCE_USE_ARM_NEON)) | ||||
| #define JUCE_USE_ARM_NEON 1 | #define JUCE_USE_ARM_NEON 1 | ||||
| #endif | |||||
| #if JUCE_USE_ARM_NEON | |||||
| #include <arm_neon.h> | #include <arm_neon.h> | ||||
| #endif | #endif | ||||
| @@ -145,7 +145,7 @@ void MidiKeyboardState::processNextMidiBuffer (MidiBuffer& buffer, | |||||
| const bool injectIndirectEvents) | const bool injectIndirectEvents) | ||||
| { | { | ||||
| MidiBuffer::Iterator i (buffer); | MidiBuffer::Iterator i (buffer); | ||||
| MidiMessage message (0xf4, 0.0); | |||||
| MidiMessage message; | |||||
| int time; | int time; | ||||
| const ScopedLock sl (lock); | const ScopedLock sl (lock); | ||||
| @@ -63,6 +63,7 @@ void SynthesiserVoice::clearCurrentNote() | |||||
| } | } | ||||
| void SynthesiserVoice::aftertouchChanged (int) {} | void SynthesiserVoice::aftertouchChanged (int) {} | ||||
| void SynthesiserVoice::channelPressureChanged (int) {} | |||||
| bool SynthesiserVoice::wasStartedBefore (const SynthesiserVoice& other) const noexcept | bool SynthesiserVoice::wasStartedBefore (const SynthesiserVoice& other) const noexcept | ||||
| { | { | ||||
| @@ -73,6 +74,7 @@ bool SynthesiserVoice::wasStartedBefore (const SynthesiserVoice& other) const no | |||||
| Synthesiser::Synthesiser() | Synthesiser::Synthesiser() | ||||
| : sampleRate (0), | : sampleRate (0), | ||||
| lastNoteOnCounter (0), | lastNoteOnCounter (0), | ||||
| minimumSubBlockSize (32), | |||||
| shouldStealNotes (true) | shouldStealNotes (true) | ||||
| { | { | ||||
| for (int i = 0; i < numElementsInArray (lastPitchWheelValues); ++i) | for (int i = 0; i < numElementsInArray (lastPitchWheelValues); ++i) | ||||
| @@ -131,6 +133,12 @@ void Synthesiser::setNoteStealingEnabled (const bool shouldSteal) | |||||
| shouldStealNotes = 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) | 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! | // must set the sample rate before using this! | ||||
| jassert (sampleRate != 0); | jassert (sampleRate != 0); | ||||
| const ScopedLock sl (lock); | |||||
| MidiBuffer::Iterator midiIterator (midiData); | MidiBuffer::Iterator midiIterator (midiData); | ||||
| midiIterator.setNextSamplePosition (startSample); | midiIterator.setNextSamplePosition (startSample); | ||||
| MidiMessage m (0xf4, 0.0); | |||||
| int midiEventPos; | |||||
| MidiMessage m; | |||||
| const ScopedLock sl (lock); | |||||
| while (numSamples > 0) | 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); | 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) | 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) | void Synthesiser::handleMidiEvent (const MidiMessage& m) | ||||
| { | { | ||||
| const int channel = m.getChannel(); | |||||
| if (m.isNoteOn()) | if (m.isNoteOn()) | ||||
| { | { | ||||
| noteOn (m.getChannel(), m.getNoteNumber(), m.getFloatVelocity()); | |||||
| noteOn (channel, m.getNoteNumber(), m.getFloatVelocity()); | |||||
| } | } | ||||
| else if (m.isNoteOff()) | 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()) | else if (m.isAllNotesOff() || m.isAllSoundOff()) | ||||
| { | { | ||||
| allNotesOff (m.getChannel(), true); | |||||
| allNotesOff (channel, true); | |||||
| } | } | ||||
| else if (m.isPitchWheel()) | else if (m.isPitchWheel()) | ||||
| { | { | ||||
| const int channel = m.getChannel(); | |||||
| const int wheelPos = m.getPitchWheelValue(); | const int wheelPos = m.getPitchWheelValue(); | ||||
| lastPitchWheelValues [channel - 1] = wheelPos; | lastPitchWheelValues [channel - 1] = wheelPos; | ||||
| handlePitchWheel (channel, wheelPos); | handlePitchWheel (channel, wheelPos); | ||||
| } | } | ||||
| else if (m.isAftertouch()) | 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()) | 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) | void Synthesiser::handleSustainPedal (int midiChannel, bool isDown) | ||||
| { | { | ||||
| jassert (midiChannel > 0 && midiChannel <= 16); | jassert (midiChannel > 0 && midiChannel <= 16); | ||||
| @@ -423,6 +467,12 @@ void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/) | |||||
| jassert (midiChannel > 0 && midiChannel <= 16); | 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, | SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, | ||||
| int midiChannel, int midiNoteNumber, | 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.. | // 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); | SynthesiserVoice* const voice = usableVoices.getUnchecked (i); | ||||
| @@ -492,8 +542,27 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, | |||||
| return voice; | 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); | SynthesiserVoice* const voice = usableVoices.getUnchecked (i); | ||||
| @@ -501,6 +570,7 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, | |||||
| return voice; | 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); | 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. | /** Renders the next block of data for this voice. | ||||
| @@ -440,6 +445,20 @@ public: | |||||
| */ | */ | ||||
| virtual void handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue); | 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. */ | /** Handles a sustain pedal event. */ | ||||
| virtual void handleSustainPedal (int midiChannel, bool isDown); | virtual void handleSustainPedal (int midiChannel, bool isDown); | ||||
| @@ -449,6 +468,13 @@ public: | |||||
| /** Can be overridden to handle soft pedal events. */ | /** Can be overridden to handle soft pedal events. */ | ||||
| virtual void handleSoftPedal (int midiChannel, bool isDown); | 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. | /** 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; } | 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: | protected: | ||||
| //============================================================================== | //============================================================================== | ||||
| /** This is used to control access to the rendering callback and the note trigger methods. */ | /** This is used to control access to the rendering callback and the note trigger methods. */ | ||||
| @@ -537,6 +579,7 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| double sampleRate; | double sampleRate; | ||||
| uint32 lastNoteOnCounter; | uint32 lastNoteOnCounter; | ||||
| int minimumSubBlockSize; | |||||
| bool shouldStealNotes; | bool shouldStealNotes; | ||||
| BigInteger sustainPedalsDown; | BigInteger sustainPedalsDown; | ||||
| @@ -551,6 +551,11 @@ double AudioDeviceManager::chooseBestSampleRate (double rate) const | |||||
| const Array<double> rates (currentAudioDevice->getAvailableSampleRates()); | const Array<double> rates (currentAudioDevice->getAvailableSampleRates()); | ||||
| if (rate > 0 && rates.contains (rate)) | |||||
| return rate; | |||||
| rate = currentAudioDevice->getCurrentSampleRate(); | |||||
| if (rate > 0 && rates.contains (rate)) | if (rate > 0 && rates.contains (rate)) | ||||
| return 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) | for (int i = 0; ratesToTry[i] != 0; ++i) | ||||
| { | { | ||||
| if (snd_pcm_hw_params_any (handle, hwParams) >= 0 | 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]); | rates.addIfNotAlreadyThere ((double) ratesToTry[i]); | ||||
| } | } | ||||
| @@ -257,10 +257,10 @@ public: | |||||
| int dir = 0; | int dir = 0; | ||||
| unsigned int periods = 4; | 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)) | 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_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_set_period_size_near (handle, hwParams, &samplesPerPeriod, &dir)) | ||||
| || JUCE_ALSA_FAILED (snd_pcm_hw_params (handle, hwParams))) | || 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))) | || JUCE_ALSA_FAILED (snd_pcm_hw_params_get_periods (hwParams, &periods, &dir))) | ||||
| latency = 0; | latency = 0; | ||||
| else | 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 | JUCE_ALSA_LOG ("frames: " << (int) frames << ", periods: " << (int) periods | ||||
| << ", samplesPerPeriod: " << (int) samplesPerPeriod); | << ", samplesPerPeriod: " << (int) samplesPerPeriod); | ||||
| @@ -316,22 +316,22 @@ public: | |||||
| if (isInterleaved) | 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) | for (int i = 0; i < numChannelsRunning; ++i) | ||||
| converter->convertSamples (scratch.getData(), i, data[i], 0, numSamples); | 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 | else | ||||
| { | { | ||||
| for (int i = 0; i < numChannelsRunning; ++i) | for (int i = 0; i < numChannelsRunning; ++i) | ||||
| converter->convertSamples (data[i], data[i], numSamples); | 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; | return false; | ||||
| if (numDone < numSamples) | if (numDone < numSamples) | ||||
| @@ -347,12 +347,12 @@ public: | |||||
| if (isInterleaved) | 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) | 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; | return false; | ||||
| if (num < numSamples) | if (num < numSamples) | ||||
| @@ -363,9 +363,9 @@ public: | |||||
| } | } | ||||
| else | 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; | return false; | ||||
| if (num < numSamples) | 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.setSize (jmax ((int) minChansOut, outputChannels.getHighestBit()) + 1, bufferSize); | ||||
| outputChannelBuffer.clear(); | outputChannelBuffer.clear(); | ||||
| @@ -557,7 +557,7 @@ public: | |||||
| return; | return; | ||||
| } | } | ||||
| ensureMinimumNumBitsSet (currentInputChans, minChansIn); | |||||
| ensureMinimumNumBitsSet (currentInputChans, (int) minChansIn); | |||||
| if (! inputDevice->setParameters ((unsigned int) sampleRate, | if (! inputDevice->setParameters ((unsigned int) sampleRate, | ||||
| jlimit ((int) minChansIn, (int) maxChansIn, currentInputChans.getHighestBit() + 1), | jlimit ((int) minChansIn, (int) maxChansIn, currentInputChans.getHighestBit() + 1), | ||||
| @@ -656,7 +656,7 @@ public: | |||||
| snd_pcm_sframes_t avail = snd_pcm_avail_update (inputDevice->handle); | snd_pcm_sframes_t avail = snd_pcm_avail_update (inputDevice->handle); | ||||
| if (avail < 0) | 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; | audioIoInProgress = true; | ||||
| @@ -688,7 +688,7 @@ public: | |||||
| else | else | ||||
| { | { | ||||
| for (int i = 0; i < outputChannelDataForCallback.size(); ++i) | 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); | snd_pcm_sframes_t avail = snd_pcm_avail_update (outputDevice->handle); | ||||
| if (avail < 0) | 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; | audioIoInProgress = true; | ||||
| @@ -1092,9 +1092,9 @@ private: | |||||
| if (snd_ctl_pcm_next_device (handle, &device) < 0 || device < 0) | if (snd_ctl_pcm_next_device (handle, &device) < 0 || device < 0) | ||||
| break; | 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_subdevice (pcmInfo, subDevice); | ||||
| snd_pcm_info_set_stream (pcmInfo, SND_PCM_STREAM_CAPTURE); | snd_pcm_info_set_stream (pcmInfo, SND_PCM_STREAM_CAPTURE); | ||||
| @@ -1118,7 +1118,7 @@ private: | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| id << "hw:" << cardId << "," << device << "," << subDevice; | |||||
| id << "hw:" << cardId << "," << device << "," << (int) subDevice; | |||||
| name << cardName << ", " << snd_pcm_info_get_name (pcmInfo) | name << cardName << ", " << snd_pcm_info_get_name (pcmInfo) | ||||
| << " {" << snd_pcm_info_get_subdevice_name (pcmInfo) << "}"; | << " {" << snd_pcm_info_get_subdevice_name (pcmInfo) << "}"; | ||||
| } | } | ||||
| @@ -133,14 +133,14 @@ private: | |||||
| if (snd_midi_event_new (maxEventSize, &midiParser) >= 0) | if (snd_midi_event_new (maxEventSize, &midiParser) >= 0) | ||||
| { | { | ||||
| const int numPfds = snd_seq_poll_descriptors_count (seqHandle, POLLIN); | 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); | HeapBlock <uint8> buffer (maxEventSize); | ||||
| while (! threadShouldExit()) | 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()) | if (threadShouldExit()) | ||||
| break; | break; | ||||
| @@ -154,14 +154,14 @@ private: | |||||
| if (snd_seq_event_input (seqHandle, &inputEvent) >= 0) | if (snd_seq_event_input (seqHandle, &inputEvent) >= 0) | ||||
| { | { | ||||
| // xxx what about SYSEXes that are too big for the buffer? | // 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); | maxEventSize, inputEvent); | ||||
| snd_midi_event_reset_decode (midiParser); | snd_midi_event_reset_decode (midiParser); | ||||
| if (numBytes > 0) | if (numBytes > 0) | ||||
| { | { | ||||
| const MidiMessage message ((const uint8*) buffer, numBytes, | |||||
| const MidiMessage message ((const uint8*) buffer, (int) numBytes, | |||||
| Time::getMillisecondCounter() * 0.001); | Time::getMillisecondCounter() * 0.001); | ||||
| client.handleIncomingMidiMessage (message, inputEvent->dest.port); | client.handleIncomingMidiMessage (message, inputEvent->dest.port); | ||||
| @@ -410,7 +410,7 @@ public: | |||||
| maxEventSize (16 * 1024) | maxEventSize (16 * 1024) | ||||
| { | { | ||||
| jassert (port.isValid() && midiOutput != nullptr); | jassert (port.isValid() && midiOutput != nullptr); | ||||
| snd_midi_event_new (maxEventSize, &midiParser); | |||||
| snd_midi_event_new ((size_t) maxEventSize, &midiParser); | |||||
| } | } | ||||
| ~MidiOutputDevice() | ~MidiOutputDevice() | ||||
| @@ -425,7 +425,7 @@ public: | |||||
| { | { | ||||
| maxEventSize = message.getRawDataSize(); | maxEventSize = message.getRawDataSize(); | ||||
| snd_midi_event_free (midiParser); | snd_midi_event_free (midiParser); | ||||
| snd_midi_event_new (maxEventSize, &midiParser); | |||||
| snd_midi_event_new ((size_t) maxEventSize, &midiParser); | |||||
| } | } | ||||
| snd_seq_event_t event; | snd_seq_event_t event; | ||||
| @@ -1067,6 +1067,12 @@ public: | |||||
| jassert (! isOpen()); | jassert (! isOpen()); | ||||
| jassert (! device->isOpen()); | jassert (! device->isOpen()); | ||||
| devices.add (new DeviceWrapper (*this, device, useInputs, useOutputs)); | devices.add (new DeviceWrapper (*this, device, useInputs, useOutputs)); | ||||
| if (currentSampleRate == 0) | |||||
| currentSampleRate = device->getCurrentSampleRate(); | |||||
| if (currentBufferSize == 0) | |||||
| currentBufferSize = device->getCurrentBufferSizeSamples(); | |||||
| } | } | ||||
| Array<AudioIODevice*> getDevices() const | 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 | 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) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ASIOAudioIODevice) | ||||
| @@ -30,9 +30,9 @@ public: | |||||
| Writer (OutputStream* destStream, const String& formatName, | Writer (OutputStream* destStream, const String& formatName, | ||||
| const File& appFile, int vbr, int cbr, | const File& appFile, int vbr, int cbr, | ||||
| double sampleRate, unsigned int numberOfChannels, | double sampleRate, unsigned int numberOfChannels, | ||||
| unsigned int bitsPerSample, const StringPairArray& metadata) | |||||
| int bitsPerSample, const StringPairArray& metadata) | |||||
| : AudioFormatWriter (destStream, formatName, sampleRate, | : AudioFormatWriter (destStream, formatName, sampleRate, | ||||
| numberOfChannels, bitsPerSample), | |||||
| numberOfChannels, (unsigned int) bitsPerSample), | |||||
| vbrLevel (vbr), cbrBitrate (cbr), | vbrLevel (vbr), cbrBitrate (cbr), | ||||
| tempWav (".wav") | tempWav (".wav") | ||||
| { | { | ||||
| @@ -426,9 +426,8 @@ struct VBRTagData | |||||
| if (flags & 4) | 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; | data += 100; | ||||
| } | } | ||||
| @@ -194,7 +194,7 @@ public: | |||||
| bufferList->mNumberBuffers = 1; | bufferList->mNumberBuffers = 1; | ||||
| bufferList->mBuffers[0].mNumberChannels = inputStreamDesc.mChannelsPerFrame; | 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); | dataBuffer.malloc (bufferList->mBuffers[0].mDataByteSize); | ||||
| bufferList->mBuffers[0].mData = dataBuffer; | bufferList->mBuffers[0].mData = dataBuffer; | ||||
| @@ -262,10 +262,10 @@ public: | |||||
| } | } | ||||
| int framesToDo = jmin (numSamples, (int) (bufferList->mBuffers[0].mDataByteSize / inputStreamDesc.mBytesPerFrame)); | 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 outFlags = 0; | ||||
| UInt32 actualNumFrames = framesToDo; | |||||
| UInt32 actualNumFrames = (UInt32) framesToDo; | |||||
| OSStatus err = MovieAudioExtractionFillBuffer (extractor, &actualNumFrames, bufferList, &outFlags); | OSStatus err = MovieAudioExtractionFillBuffer (extractor, &actualNumFrames, bufferList, &outFlags); | ||||
| if (err != noErr) | if (err != noErr) | ||||
| { | { | ||||
| @@ -274,7 +274,7 @@ public: | |||||
| } | } | ||||
| lastSampleRead = startSampleInFile + actualNumFrames; | lastSampleRead = startSampleInFile + actualNumFrames; | ||||
| const int samplesReceived = actualNumFrames; | |||||
| const int samplesReceived = (int) actualNumFrames; | |||||
| for (int j = numDestChannels; --j >= 0;) | for (int j = numDestChannels; --j >= 0;) | ||||
| { | { | ||||
| @@ -298,7 +298,7 @@ public: | |||||
| { | { | ||||
| for (int j = numDestChannels; --j >= 0;) | for (int j = numDestChannels; --j >= 0;) | ||||
| if (destSamples[j] != nullptr) | if (destSamples[j] != nullptr) | ||||
| zeromem (destSamples[j] + startOffsetInDestBuffer, sizeof (int) * numSamples); | |||||
| zeromem (destSamples[j] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples); | |||||
| break; | break; | ||||
| } | } | ||||
| @@ -609,16 +609,18 @@ namespace WavFileHelpers | |||||
| { | { | ||||
| static MemoryBlock createFrom (const StringPairArray& values) | 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()) | 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 */ | /* generate the partition's first stage cascade value */ | ||||
| if(csubbits){ | if(csubbits){ | ||||
| int maxval[8]; | |||||
| int maxval[8] = { 0 }; | |||||
| for(k=0;k<csub;k++){ | for(k=0;k<csub;k++){ | ||||
| int booknum=info->class_subbook[classx][k]; | int booknum=info->class_subbook[classx][k]; | ||||
| if(booknum<0){ | if(booknum<0){ | ||||
| @@ -119,7 +119,7 @@ public: | |||||
| should then be deleted by the caller. | should then be deleted by the caller. | ||||
| If the stream can't be created for some reason (e.g. the parameters passed in | 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 | @param streamToWriteTo the stream that the data will go to - this will be | ||||
| deleted by the AudioFormatWriter object when it's no longer | deleted by the AudioFormatWriter object when it's no longer | ||||
| @@ -58,10 +58,10 @@ public: | |||||
| @see isLooping | @see isLooping | ||||
| */ | */ | ||||
| void setLooping (bool shouldLoop); | |||||
| void setLooping (bool shouldLoop) override; | |||||
| /** Returns whether loop-mode is turned on or not. */ | /** 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. */ | /** Returns the reader that's being used. */ | ||||
| AudioFormatReader* getAudioFormatReader() const noexcept { return reader; } | AudioFormatReader* getAudioFormatReader() const noexcept { return reader; } | ||||
| @@ -126,7 +126,7 @@ public: | |||||
| void startNote (int midiNoteNumber, float velocity, SynthesiserSound*, int pitchWheel) override; | void startNote (int midiNoteNumber, float velocity, SynthesiserSound*, int pitchWheel) override; | ||||
| void stopNote (float velocity, bool allowTailOff) 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 controllerMoved (int controllerNumber, int newValue) override; | ||||
| void renderNextBlock (AudioSampleBuffer&, int startSample, int numSamples) override; | void renderNextBlock (AudioSampleBuffer&, int startSample, int numSamples) override; | ||||
| @@ -184,7 +184,7 @@ namespace AudioUnitFormatHelpers | |||||
| const char* const utf8 = fileOrIdentifier.toUTF8(); | const char* const utf8 = fileOrIdentifier.toUTF8(); | ||||
| if (CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, | if (CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, | ||||
| strlen (utf8), file.isDirectory())) | |||||
| (CFIndex) strlen (utf8), file.isDirectory())) | |||||
| { | { | ||||
| CFBundleRef bundleRef = CFBundleCreate (kCFAllocatorDefault, url); | CFBundleRef bundleRef = CFBundleCreate (kCFAllocatorDefault, url); | ||||
| CFRelease (url); | CFRelease (url); | ||||
| @@ -345,8 +345,8 @@ public: | |||||
| refreshParameterList(); | refreshParameterList(); | ||||
| updateNumChannels(); | updateNumChannels(); | ||||
| producesMidiMessages = canProduceMidiOutput(); | producesMidiMessages = canProduceMidiOutput(); | ||||
| setPlayConfigDetails (numInputBusChannels * numInputBusses, | |||||
| numOutputBusChannels * numOutputBusses, | |||||
| setPlayConfigDetails ((int) (numInputBusChannels * numInputBusses), | |||||
| (int) (numOutputBusChannels * numOutputBusses), | |||||
| rate, blockSize); | rate, blockSize); | ||||
| setLatencySamples (0); | setLatencySamples (0); | ||||
| @@ -420,7 +420,7 @@ public: | |||||
| UInt32 sampleRateSize = sizeof (sampleRateIn); | UInt32 sampleRateSize = sizeof (sampleRateIn); | ||||
| const Float64 sr = newSampleRate; | 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); | AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, i, &sampleRateIn, &sampleRateSize); | ||||
| @@ -428,7 +428,7 @@ public: | |||||
| AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, i, &sr, sizeof (sr)); | 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); | AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, i, &sampleRateOut, &sampleRateSize); | ||||
| @@ -440,9 +440,9 @@ public: | |||||
| AudioUnitSetProperty (audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, | AudioUnitSetProperty (audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, | ||||
| &frameSize, sizeof (frameSize)); | &frameSize, sizeof (frameSize)); | ||||
| setPlayConfigDetails (numInputBusChannels * numInputBusses, | |||||
| numOutputBusChannels * numOutputBusses, | |||||
| newSampleRate, estimatedSamplesPerBlock); | |||||
| setPlayConfigDetails ((int) (numInputBusChannels * numInputBusses), | |||||
| (int) (numOutputBusChannels * numOutputBusses), | |||||
| (double) newSampleRate, estimatedSamplesPerBlock); | |||||
| Float64 latencySecs = 0.0; | Float64 latencySecs = 0.0; | ||||
| UInt32 latencySize = sizeof (latencySecs); | UInt32 latencySize = sizeof (latencySecs); | ||||
| @@ -463,13 +463,13 @@ public: | |||||
| stream.mBitsPerChannel = 32; | stream.mBitsPerChannel = 32; | ||||
| stream.mChannelsPerFrame = numInputBusChannels; | stream.mChannelsPerFrame = numInputBusChannels; | ||||
| for (int i = 0; i < numInputBusses; ++i) | |||||
| for (AudioUnitElement i = 0; i < numInputBusses; ++i) | |||||
| AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, | AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, | ||||
| kAudioUnitScope_Input, i, &stream, sizeof (stream)); | kAudioUnitScope_Input, i, &stream, sizeof (stream)); | ||||
| stream.mChannelsPerFrame = numOutputBusChannels; | stream.mChannelsPerFrame = numOutputBusChannels; | ||||
| for (int i = 0; i < numOutputBusses; ++i) | |||||
| for (AudioUnitElement i = 0; i < numOutputBusses; ++i) | |||||
| AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, | AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, | ||||
| kAudioUnitScope_Output, i, &stream, sizeof (stream)); | kAudioUnitScope_Output, i, &stream, sizeof (stream)); | ||||
| } | } | ||||
| @@ -518,8 +518,8 @@ public: | |||||
| void resetBusses() | 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 | void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override | ||||
| @@ -530,17 +530,17 @@ public: | |||||
| { | { | ||||
| timeStamp.mHostTime = AudioGetCurrentHostTime(); | timeStamp.mHostTime = AudioGetCurrentHostTime(); | ||||
| for (int i = 0; i < numOutputBusses; ++i) | |||||
| for (AudioUnitElement i = 0; i < numOutputBusses; ++i) | |||||
| { | { | ||||
| if (AudioBufferList* const abl = getAudioBufferListForBus(i)) | if (AudioBufferList* const abl = getAudioBufferListForBus(i)) | ||||
| { | { | ||||
| abl->mNumberBuffers = numOutputBusChannels; | 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].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) | if (midiEventSize <= 3) | ||||
| MusicDeviceMIDIEvent (audioUnit, | MusicDeviceMIDIEvent (audioUnit, | ||||
| midiEventData[0], midiEventData[1], midiEventData[2], | midiEventData[0], midiEventData[1], midiEventData[2], | ||||
| midiEventPosition); | |||||
| (UInt32) midiEventPosition); | |||||
| else | else | ||||
| MusicDeviceSysEx (audioUnit, midiEventData, midiEventSize); | |||||
| MusicDeviceSysEx (audioUnit, midiEventData, (UInt32) midiEventSize); | |||||
| } | } | ||||
| midiMessages.clear(); | midiMessages.clear(); | ||||
| } | } | ||||
| for (int i = 0; i < numOutputBusses; ++i) | |||||
| for (AudioUnitElement i = 0; i < numOutputBusses; ++i) | |||||
| { | { | ||||
| AudioUnitRenderActionFlags flags = 0; | AudioUnitRenderActionFlags flags = 0; | ||||
| AudioUnitRender (audioUnit, &flags, &timeStamp, i, numSamples, getAudioBufferListForBus (i)); | |||||
| AudioUnitRender (audioUnit, &flags, &timeStamp, i, (UInt32) numSamples, getAudioBufferListForBus (i)); | |||||
| } | } | ||||
| timeStamp.mSampleTime += numSamples; | timeStamp.mSampleTime += numSamples; | ||||
| @@ -796,7 +796,7 @@ public: | |||||
| CFDataRef data = (CFDataRef) CFWriteStreamCopyProperty (stream, kCFStreamPropertyDataWritten); | CFDataRef data = (CFDataRef) CFWriteStreamCopyProperty (stream, kCFStreamPropertyDataWritten); | ||||
| destData.setSize (bytesWritten); | |||||
| destData.setSize ((size_t) bytesWritten); | |||||
| destData.copyFrom (CFDataGetBytePtr (data), 0, destData.getSize()); | destData.copyFrom (CFDataGetBytePtr (data), 0, destData.getSize()); | ||||
| CFRelease (data); | CFRelease (data); | ||||
| @@ -911,7 +911,7 @@ private: | |||||
| HeapBlock <AudioBufferList> outputBufferList; | HeapBlock <AudioBufferList> outputBufferList; | ||||
| AudioTimeStamp timeStamp; | AudioTimeStamp timeStamp; | ||||
| AudioSampleBuffer* currentBuffer; | AudioSampleBuffer* currentBuffer; | ||||
| int numInputBusChannels, numOutputBusChannels, numInputBusses, numOutputBusses; | |||||
| AudioUnitElement numInputBusChannels, numOutputBusChannels, numInputBusses, numOutputBusses; | |||||
| AudioUnit audioUnit; | AudioUnit audioUnit; | ||||
| AUEventListenerRef eventListenerRef; | AUEventListenerRef eventListenerRef; | ||||
| @@ -941,7 +941,7 @@ private: | |||||
| info.inputProcRefCon = this; | info.inputProcRefCon = this; | ||||
| info.inputProc = renderGetInputCallback; | info.inputProc = renderGetInputCallback; | ||||
| for (int i = 0; i < numInputBusses; ++i) | |||||
| for (AudioUnitElement i = 0; i < numInputBusses; ++i) | |||||
| AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, | AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, | ||||
| kAudioUnitScope_Input, i, &info, sizeof (info)); | kAudioUnitScope_Input, i, &info, sizeof (info)); | ||||
| } | } | ||||
| @@ -1028,11 +1028,11 @@ private: | |||||
| break; | break; | ||||
| case kAudioUnitEvent_BeginParameterChangeGesture: | case kAudioUnitEvent_BeginParameterChangeGesture: | ||||
| beginParameterChangeGesture (event.mArgument.mParameter.mParameterID); | |||||
| beginParameterChangeGesture ((int) event.mArgument.mParameter.mParameterID); | |||||
| break; | break; | ||||
| case kAudioUnitEvent_EndParameterChangeGesture: | case kAudioUnitEvent_EndParameterChangeGesture: | ||||
| endParameterChangeGesture (event.mArgument.mParameter.mParameterID); | |||||
| endParameterChangeGesture ((int) event.mArgument.mParameter.mParameterID); | |||||
| break; | break; | ||||
| default: | default: | ||||
| @@ -1062,7 +1062,7 @@ private: | |||||
| for (UInt32 i = 0; i < ioData->mNumberBuffers; ++i) | 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()) | if (bufferChannel < currentBuffer->getNumChannels()) | ||||
| { | { | ||||
| @@ -1131,16 +1131,16 @@ private: | |||||
| if (ph != nullptr && ph->getCurrentPosition (result)) | 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 | setIfNotNull (outCurrentMeasureDownBeat, result.ppqPositionOfLastBarStart); //xxx wrong | ||||
| } | } | ||||
| else | 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); | setIfNotNull (outCurrentMeasureDownBeat, 0); | ||||
| } | } | ||||
| @@ -1226,12 +1226,12 @@ private: | |||||
| return offsetof (AudioBufferList, mBuffers) + (sizeof (AudioBuffer) * numOutputBusChannels); | 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); | return addBytesToPointer (outputBufferList.getData(), getAudioBufferSizeInBytes() * busIndex); | ||||
| } | } | ||||
| int getElementCount (AudioUnitScope scope) const noexcept | |||||
| AudioUnitElement getElementCount (AudioUnitScope scope) const noexcept | |||||
| { | { | ||||
| UInt32 count; | UInt32 count; | ||||
| UInt32 countSize = sizeof (count); | UInt32 countSize = sizeof (count); | ||||
| @@ -1240,7 +1240,7 @@ private: | |||||
| || countSize == 0) | || countSize == 0) | ||||
| count = 1; | count = 1; | ||||
| return (int) count; | |||||
| return count; | |||||
| } | } | ||||
| void updateNumChannels() | void updateNumChannels() | ||||
| @@ -1266,17 +1266,17 @@ private: | |||||
| const int outChannels = (int) supportedChannels[i].outChannels; | const int outChannels = (int) supportedChannels[i].outChannels; | ||||
| if (inChannels < 0) | if (inChannels < 0) | ||||
| maximumNumIns = jmin (maximumNumIns, inChannels); | |||||
| maximumNumIns = jmin (maximumNumIns, inChannels); | |||||
| else | else | ||||
| explicitNumIns = jmax (explicitNumIns, inChannels); | explicitNumIns = jmax (explicitNumIns, inChannels); | ||||
| if (outChannels < 0) | if (outChannels < 0) | ||||
| maximumNumOuts = jmin (maximumNumOuts, outChannels); | |||||
| maximumNumOuts = jmin (maximumNumOuts, outChannels); | |||||
| else | else | ||||
| explicitNumOuts = jmax (explicitNumOuts, outChannels); | 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 == -2 && maximumNumOuts == -1) // (special meaning: any number of ins/outs, even if they don't match) | ||||
| || (maximumNumIns == -1 && maximumNumOuts == -2)) | || (maximumNumIns == -1 && maximumNumOuts == -2)) | ||||
| { | { | ||||
| @@ -1284,8 +1284,8 @@ private: | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| numInputBusChannels = explicitNumIns; | |||||
| numOutputBusChannels = explicitNumOuts; | |||||
| numInputBusChannels = (AudioUnitElement) explicitNumIns; | |||||
| numOutputBusChannels = (AudioUnitElement) explicitNumOuts; | |||||
| if (maximumNumIns == -1 || (maximumNumIns < 0 && explicitNumIns <= -maximumNumIns)) | if (maximumNumIns == -1 || (maximumNumIns < 0 && explicitNumIns <= -maximumNumIns)) | ||||
| numInputBusChannels = 2; | numInputBusChannels = 2; | ||||
| @@ -26,7 +26,7 @@ | |||||
| } // (juce namespace) | } // (juce namespace) | ||||
| #include <ladspa.h> | |||||
| #include "ladspa.h" | |||||
| namespace juce | namespace juce | ||||
| { | { | ||||
| @@ -250,7 +250,7 @@ public: | |||||
| break; | break; | ||||
| case Steinberg::Vst::Event::kDataEvent: | 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); | e.sampleOffset); | ||||
| break; | break; | ||||
| @@ -300,7 +300,7 @@ public: | |||||
| { | { | ||||
| e.type = Steinberg::Vst::Event::kDataEvent; | e.type = Steinberg::Vst::Event::kDataEvent; | ||||
| e.data.bytes = msg.getSysExData(); | e.data.bytes = msg.getSysExData(); | ||||
| e.data.size = msg.getSysExDataSize(); | |||||
| e.data.size = (uint32) msg.getSysExDataSize(); | |||||
| e.data.type = Steinberg::Vst::DataEvent::kMidiSysEx; | e.data.type = Steinberg::Vst::DataEvent::kMidiSysEx; | ||||
| } | } | ||||
| else if (msg.isAftertouch()) | else if (msg.isAftertouch()) | ||||
| @@ -488,7 +488,7 @@ public: | |||||
| const char* const utf8 = file.getFullPathName().toRawUTF8(); | const char* const utf8 = file.getFullPathName().toRawUTF8(); | ||||
| if (CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, | if (CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, | ||||
| strlen (utf8), file.isDirectory())) | |||||
| (CFIndex) strlen (utf8), file.isDirectory())) | |||||
| { | { | ||||
| bundleRef = CFBundleCreate (kCFAllocatorDefault, url); | bundleRef = CFBundleCreate (kCFAllocatorDefault, url); | ||||
| CFRelease (url); | CFRelease (url); | ||||
| @@ -864,7 +864,7 @@ public: | |||||
| wantsMidiMessages = dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0; | wantsMidiMessages = dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0; | ||||
| #if JUCE_MAC && JUCE_SUPPORT_CARBON | #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 | #endif | ||||
| setLatencySamples (effect->initialDelay); | setLatencySamples (effect->initialDelay); | ||||
| @@ -987,27 +987,28 @@ public: | |||||
| if (AudioPlayHead* const playHead = getPlayHead()) | if (AudioPlayHead* const playHead = getPlayHead()) | ||||
| { | { | ||||
| AudioPlayHead::CurrentPositionInfo position; | 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::fps24: setHostTimeFrameRate (0, 24.0, position.timeInSeconds); break; | ||||
| case AudioPlayHead::fps25: setHostTimeFrameRate (1, 25.0, position.timeInSeconds); break; | case AudioPlayHead::fps25: setHostTimeFrameRate (1, 25.0, position.timeInSeconds); break; | ||||
| case AudioPlayHead::fps2997: setHostTimeFrameRate (2, 29.97, 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::fps2997drop: setHostTimeFrameRate (4, 29.97, position.timeInSeconds); break; | ||||
| case AudioPlayHead::fps30drop: setHostTimeFrameRate (5, 29.97, position.timeInSeconds); break; | case AudioPlayHead::fps30drop: setHostTimeFrameRate (5, 29.97, position.timeInSeconds); break; | ||||
| default: 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 getStateInformation (MemoryBlock& mb) override { saveToFXBFile (mb, true); } | ||||
| void getCurrentProgramStateInformation (MemoryBlock& mb) override { saveToFXBFile (mb, false); } | 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 | void timerCallback() override | ||||
| @@ -1282,7 +1284,12 @@ public: | |||||
| case audioMasterSizeWindow: | case audioMasterSizeWindow: | ||||
| if (AudioProcessorEditor* ed = getActiveEditor()) | if (AudioProcessorEditor* ed = getActiveEditor()) | ||||
| ed->setSize (index, (int) value); | |||||
| { | |||||
| #if JUCE_LINUX | |||||
| const MessageManagerLock mmLock; | |||||
| #endif | |||||
| ed->setSize (index, (int) value); | |||||
| } | |||||
| return 1; | return 1; | ||||
| @@ -1356,6 +1363,7 @@ public: | |||||
| "receiveVstEvents", | "receiveVstEvents", | ||||
| "receiveVstMidiEvent", | "receiveVstMidiEvent", | ||||
| "supportShell", | "supportShell", | ||||
| "sizeWindow", | |||||
| "shellCategory" }; | "shellCategory" }; | ||||
| for (int i = 0; i < numElementsInArray (canDos); ++i) | for (int i = 0; i < numElementsInArray (canDos); ++i) | ||||
| @@ -1449,7 +1457,7 @@ public: | |||||
| { | { | ||||
| const int oldProg = getCurrentProgram(); | const int oldProg = getCurrentProgram(); | ||||
| const int numParams = fxbSwap (((const fxProgram*) (set->programs))->numParams); | 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) | for (int i = 0; i < fxbSwap (set->numPrograms); ++i) | ||||
| { | { | ||||
| @@ -1496,7 +1504,7 @@ public: | |||||
| // non-preset chunk | // non-preset chunk | ||||
| const fxChunkSet* const cset = (const fxChunkSet*) data; | 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; | return false; | ||||
| setChunkData (cset->chunk, fxbSwap (cset->chunkSize), false); | setChunkData (cset->chunk, fxbSwap (cset->chunkSize), false); | ||||
| @@ -1506,7 +1514,7 @@ public: | |||||
| // preset chunk | // preset chunk | ||||
| const fxProgramSet* const cset = (const fxProgramSet*) data; | 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; | return false; | ||||
| setChunkData (cset->chunk, fxbSwap (cset->chunkSize), true); | setChunkData (cset->chunk, fxbSwap (cset->chunkSize), true); | ||||
| @@ -1571,8 +1579,8 @@ public: | |||||
| { | { | ||||
| if (isFXB) | 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); | dest.setSize (len, true); | ||||
| fxSet* const set = (fxSet*) dest.getData(); | fxSet* const set = (fxSet*) dest.getData(); | ||||
| @@ -1584,11 +1592,13 @@ public: | |||||
| set->fxVersion = fxbSwap (getVersionNumber()); | set->fxVersion = fxbSwap (getVersionNumber()); | ||||
| set->numPrograms = fxbSwap (numPrograms); | set->numPrograms = fxbSwap (numPrograms); | ||||
| const int oldProgram = getCurrentProgram(); | |||||
| MemoryBlock oldSettings; | MemoryBlock oldSettings; | ||||
| createTempParameterStore (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) | for (int i = 0; i < numPrograms; ++i) | ||||
| { | { | ||||
| @@ -1599,14 +1609,14 @@ public: | |||||
| } | } | ||||
| } | } | ||||
| setCurrentProgram (oldProgram); | |||||
| if (oldProgram >= 0) | |||||
| setCurrentProgram (oldProgram); | |||||
| restoreFromTempParameterStore (oldSettings); | restoreFromTempParameterStore (oldSettings); | ||||
| } | } | ||||
| else | 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()); | setParamsInProgramBlock ((fxProgram*) dest.getData()); | ||||
| } | } | ||||
| } | } | ||||
| @@ -1621,7 +1631,7 @@ public: | |||||
| if (usesChunks()) | if (usesChunks()) | ||||
| { | { | ||||
| void* data = nullptr; | 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) | if (data != nullptr && bytes <= maxSizeMB * 1024 * 1024) | ||||
| { | { | ||||
| @@ -1783,7 +1793,7 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| void createTempParameterStore (MemoryBlock& dest) | void createTempParameterStore (MemoryBlock& dest) | ||||
| { | { | ||||
| dest.setSize (64 + 4 * getNumParameters()); | |||||
| dest.setSize (64 + 4 * (size_t) getNumParameters()); | |||||
| dest.fillWith (0); | dest.fillWith (0); | ||||
| getCurrentProgramName().copyToUTF8 ((char*) dest.getData(), 63); | getCurrentProgramName().copyToUTF8 ((char*) dest.getData(), 63); | ||||
| @@ -1821,21 +1831,21 @@ private: | |||||
| String s; | String s; | ||||
| if (v == 0 || (int) v == -1) | if (v == 0 || (int) v == -1) | ||||
| v = getVersionNumber(); | |||||
| v = (unsigned int) getVersionNumber(); | |||||
| if (v != 0) | if (v != 0) | ||||
| { | { | ||||
| int versionBits[32]; | int versionBits[32]; | ||||
| int n = 0; | int n = 0; | ||||
| for (int vv = v; vv != 0; vv /= 10) | |||||
| for (unsigned int vv = v; vv != 0; vv /= 10) | |||||
| versionBits [n++] = vv % 10; | versionBits [n++] = vv % 10; | ||||
| if (n > 4) // if the number ends up silly, it's probably encoded as hex instead of decimal.. | if (n > 4) // if the number ends up silly, it's probably encoded as hex instead of decimal.. | ||||
| { | { | ||||
| n = 0; | n = 0; | ||||
| for (int vv = v; vv != 0; vv >>= 8) | |||||
| for (unsigned int vv = v; vv != 0; vv >>= 8) | |||||
| versionBits [n++] = vv & 255; | versionBits [n++] = vv & 255; | ||||
| } | } | ||||
| @@ -1968,9 +1978,13 @@ public: | |||||
| #elif JUCE_LINUX | #elif JUCE_LINUX | ||||
| if (pluginWindow != 0) | 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); | XMapRaised (display, pluginWindow); | ||||
| XFlush (display); | |||||
| } | } | ||||
| #endif | #endif | ||||
| @@ -2081,6 +2095,16 @@ public: | |||||
| plugin.dispatch (effEditIdle, 0, 0, 0, 0); | plugin.dispatch (effEditIdle, 0, 0, 0, 0); | ||||
| reentrant = false; | reentrant = false; | ||||
| } | } | ||||
| #if JUCE_LINUX | |||||
| if (pluginWindow == 0) | |||||
| { | |||||
| updatePluginWindowHandle(); | |||||
| if (pluginWindow != 0) | |||||
| componentMovedOrResized (true, true); | |||||
| } | |||||
| #endif | |||||
| } | } | ||||
| } | } | ||||
| @@ -2266,11 +2290,7 @@ private: | |||||
| } | } | ||||
| #elif JUCE_LINUX | #elif JUCE_LINUX | ||||
| pluginWindow = getChildWindow ((Window) getWindowHandle()); | |||||
| if (pluginWindow != 0) | |||||
| pluginProc = (EventProcPtr) getPropertyFromXWindow (pluginWindow, | |||||
| XInternAtom (display, "_XEventProc", False)); | |||||
| updatePluginWindowHandle(); | |||||
| int w = 250, h = 150; | int w = 250, h = 150; | ||||
| @@ -2498,6 +2518,15 @@ private: | |||||
| sendEventToChild (ev); | sendEventToChild (ev); | ||||
| } | } | ||||
| } | } | ||||
| void updatePluginWindowHandle() | |||||
| { | |||||
| pluginWindow = getChildWindow ((Window) getWindowHandle()); | |||||
| if (pluginWindow != 0) | |||||
| pluginProc = (EventProcPtr) getPropertyFromXWindow (pluginWindow, | |||||
| XInternAtom (display, "_XEventProc", False)); | |||||
| } | |||||
| #endif | #endif | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -128,7 +128,9 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Fills-in the given structure with details about the transport's | /** 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 | 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 | times will produce undefined behaviour, as the host may not have any context | ||||
| @@ -50,7 +50,7 @@ AudioProcessor::~AudioProcessor() | |||||
| jassert (activeEditor == nullptr); | jassert (activeEditor == nullptr); | ||||
| #endif | #endif | ||||
| #if JUCE_DEBUG | |||||
| #if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING | |||||
| // This will fail if you've called beginParameterChangeGesture() for one | // This will fail if you've called beginParameterChangeGesture() for one | ||||
| // or more parameters without having made a corresponding call to endParameterChangeGesture... | // or more parameters without having made a corresponding call to endParameterChangeGesture... | ||||
| jassert (changingParams.countNumberOfSetBits() == 0); | jassert (changingParams.countNumberOfSetBits() == 0); | ||||
| @@ -144,7 +144,7 @@ void AudioProcessor::beginParameterChangeGesture (int parameterIndex) | |||||
| { | { | ||||
| if (isPositiveAndBelow (parameterIndex, getNumParameters())) | 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 | // 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. | // call to endParameterChangeGesture. That might be fine in most hosts, but better to avoid doing it. | ||||
| jassert (! changingParams [parameterIndex]); | jassert (! changingParams [parameterIndex]); | ||||
| @@ -165,9 +165,9 @@ void AudioProcessor::endParameterChangeGesture (int parameterIndex) | |||||
| { | { | ||||
| if (isPositiveAndBelow (parameterIndex, getNumParameters())) | 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 | // 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. | // calls matched correctly. | ||||
| jassert (changingParams [parameterIndex]); | jassert (changingParams [parameterIndex]); | ||||
| changingParams.clearBit (parameterIndex); | changingParams.clearBit (parameterIndex); | ||||
| @@ -384,10 +384,17 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| /** This must return the correct value immediately after the object has been | /** This must return the correct value immediately after the object has been | ||||
| created, and mustn't change the number of parameters later. | 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(); | 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); | virtual const String getParameterName (int parameterIndex); | ||||
| /** Called by the host to find out the value of one of the filter's parameters. | /** 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. | 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 | It's also likely to be called by non-UI threads, so the code in here should | ||||
| be thread-aware. | 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); | 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. | /** 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 | 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 | will look better in constrained spaces (e.g. the displays on hardware controller | ||||
| devices or mixing desks) then you should implement this method. | devices or mixing desks) then you should implement this method. | ||||
| If you don't override it, the default implementation will call getParameterText(int), | If you don't override it, the default implementation will call getParameterText(int), | ||||
| and truncate the result. | 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); | 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. | /** 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 | 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 | will look better in constrained spaces (e.g. the displays on hardware controller | ||||
| devices or mixing desks) then you should implement this method. | devices or mixing desks) then you should implement this method. | ||||
| If you don't override it, the default implementation will call getParameterText(int), | If you don't override it, the default implementation will call getParameterText(int), | ||||
| and truncate the result. | 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); | virtual String getParameterText (int parameterIndex, int maximumStringLength); | ||||
| @@ -426,10 +445,16 @@ public: | |||||
| AudioProcessor::getDefaultNumParameterSteps(). | AudioProcessor::getDefaultNumParameterSteps(). | ||||
| If your parameter is boolean, then you may want to make this return 2. | 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. | 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); | virtual int getParameterNumSteps (int parameterIndex); | ||||
| /** Returns the default number of steps for a parameter. | /** 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 | @see getParameterNumSteps | ||||
| */ | */ | ||||
| static int getDefaultNumParameterSteps() noexcept; | static int getDefaultNumParameterSteps() noexcept; | ||||
| @@ -437,16 +462,25 @@ public: | |||||
| /** Returns the default value for the parameter. | /** Returns the default value for the parameter. | ||||
| By default, this just returns 0. | By default, this just returns 0. | ||||
| The value that is returned may or may not be used, depending on the host. | 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); | virtual float getParameterDefaultValue (int parameterIndex); | ||||
| /** Some plugin types may be able to return a label string for a | /** Some plugin types may be able to return a label string for a | ||||
| parameter's units. | 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; | virtual String getParameterLabel (int index) const; | ||||
| /** This can be overridden to tell the host that particular parameters operate in the | /** 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). | 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; | virtual bool isParameterOrientationInverted (int index) const; | ||||
| @@ -462,6 +496,9 @@ public: | |||||
| won't be able to automate your parameters properly. | won't be able to automate your parameters properly. | ||||
| The value passed will be between 0 and 1.0. | 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); | 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 | Note that to make sure the host correctly handles automation, you should call | ||||
| the beginParameterChangeGesture() and endParameterChangeGesture() methods to | the beginParameterChangeGesture() and endParameterChangeGesture() methods to | ||||
| tell the host when the user has started and stopped changing the parameter. | 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); | void setParameterNotifyingHost (int parameterIndex, float newValue); | ||||
| /** Returns true if the host can automate this parameter. | /** Returns true if the host can automate this parameter. | ||||
| By default, this returns true for all parameters. | 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; | virtual bool isParameterAutomatable (int parameterIndex) const; | ||||
| @@ -486,6 +529,9 @@ public: | |||||
| A meta-parameter is a parameter that changes other params. It is used | A meta-parameter is a parameter that changes other params. It is used | ||||
| by some hosts (e.g. AudioUnit hosts). | by some hosts (e.g. AudioUnit hosts). | ||||
| By default this returns false. | 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; | virtual bool isMetaParameter (int parameterIndex) const; | ||||
| @@ -496,6 +542,9 @@ public: | |||||
| it may use this information to help it record automation. | it may use this information to help it record automation. | ||||
| If you call this, it must be matched by a later call to endParameterChangeGesture(). | 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); | void beginParameterChangeGesture (int parameterIndex); | ||||
| @@ -505,6 +554,9 @@ public: | |||||
| it may use this information to help it record automation. | it may use this information to help it record automation. | ||||
| A call to this method must follow a call to beginParameterChangeGesture(). | 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); | void endParameterChangeGesture (int parameterIndex); | ||||
| @@ -693,7 +745,7 @@ private: | |||||
| OwnedArray<AudioProcessorParameter> managedParameters; | OwnedArray<AudioProcessorParameter> managedParameters; | ||||
| AudioProcessorParameter* getParamChecked (int) const noexcept; | AudioProcessorParameter* getParamChecked (int) const noexcept; | ||||
| #if JUCE_DEBUG | |||||
| #if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING | |||||
| BigInteger changingParams; | BigInteger changingParams; | ||||
| #endif | #endif | ||||
| @@ -308,7 +308,7 @@ public: | |||||
| void fillInPluginDescription (PluginDescription&) const override; | void fillInPluginDescription (PluginDescription&) const override; | ||||
| void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock) override; | void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock) override; | ||||
| void releaseResources() override; | void releaseResources() override; | ||||
| void processBlock (AudioSampleBuffer&, MidiBuffer&); | |||||
| void processBlock (AudioSampleBuffer&, MidiBuffer&) override; | |||||
| const String getInputChannelName (int channelIndex) const override; | const String getInputChannelName (int channelIndex) const override; | ||||
| const String getOutputChannelName (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. | /** 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 | 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. | 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 | Note that to make sure the host correctly handles automation, you should call | ||||
| @@ -85,7 +85,7 @@ public: | |||||
| void deleteKeyPressed (int) override | void deleteKeyPressed (int) override | ||||
| { | { | ||||
| owner.removeSelected(); | |||||
| owner.removeSelectedPlugins(); | |||||
| } | } | ||||
| void sortOrderChanged (int newSortColumnId, bool isForwards) override | 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) | static String getPluginDescription (const PluginDescription& desc) | ||||
| { | { | ||||
| StringArray items; | StringArray items; | ||||
| @@ -179,6 +171,12 @@ void PluginListComponent::setOptionsButtonText (const String& newText) | |||||
| resized(); | resized(); | ||||
| } | } | ||||
| void PluginListComponent::setScanDialogText (const String& title, const String& content) | |||||
| { | |||||
| dialogTitle = title; | |||||
| dialogText = content; | |||||
| } | |||||
| void PluginListComponent::setNumberOfThreadsForScanning (int num) | void PluginListComponent::setNumberOfThreadsForScanning (int num) | ||||
| { | { | ||||
| numThreads = num; | numThreads = num; | ||||
| @@ -207,13 +205,24 @@ void PluginListComponent::updateList() | |||||
| table.repaint(); | table.repaint(); | ||||
| } | } | ||||
| void PluginListComponent::removeSelected() | |||||
| void PluginListComponent::removeSelectedPlugins() | |||||
| { | { | ||||
| const SparseSet<int> selected (table.getSelectedRows()); | const SparseSet<int> selected (table.getSelectedRows()); | ||||
| for (int i = table.getNumRows(); --i >= 0;) | for (int i = table.getNumRows(); --i >= 0;) | ||||
| if (selected.contains (i)) | 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 | bool PluginListComponent::canShowSelectedFolder() const | ||||
| @@ -238,6 +247,14 @@ void PluginListComponent::removeMissingPlugins() | |||||
| list.removeType (i); | 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) | void PluginListComponent::optionsMenuStaticCallback (int result, PluginListComponent* pluginList) | ||||
| { | { | ||||
| if (pluginList != nullptr) | if (pluginList != nullptr) | ||||
| @@ -250,7 +267,7 @@ void PluginListComponent::optionsMenuCallback (int result) | |||||
| { | { | ||||
| case 0: break; | case 0: break; | ||||
| case 1: list.clear(); break; | case 1: list.clear(); break; | ||||
| case 2: removeSelected(); break; | |||||
| case 2: removeSelectedPlugins(); break; | |||||
| case 3: showSelectedFolder(); break; | case 3: showSelectedFolder(); break; | ||||
| case 4: removeMissingPlugins(); break; | case 4: removeMissingPlugins(); break; | ||||
| @@ -293,7 +310,7 @@ bool PluginListComponent::isInterestedInFileDrag (const StringArray& /*files*/) | |||||
| void PluginListComponent::filesDropped (const StringArray& files, int, int) | void PluginListComponent::filesDropped (const StringArray& files, int, int) | ||||
| { | { | ||||
| OwnedArray <PluginDescription> typesFound; | |||||
| OwnedArray<PluginDescription> typesFound; | |||||
| list.scanAndAddDragAndDroppedFiles (formatManager, files, typesFound); | list.scanAndAddDragAndDroppedFiles (formatManager, files, typesFound); | ||||
| } | } | ||||
| @@ -313,11 +330,11 @@ void PluginListComponent::setLastSearchPath (PropertiesFile& properties, AudioPl | |||||
| class PluginListComponent::Scanner : private Timer | class PluginListComponent::Scanner : private Timer | ||||
| { | { | ||||
| public: | 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), | : owner (plc), formatToScan (format), propertiesToUse (properties), | ||||
| pathChooserWindow (TRANS("Select folders to scan..."), String::empty, AlertWindow::NoIcon), | 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) | progress (0.0), numThreads (threads), finished (false) | ||||
| { | { | ||||
| FileSearchPath path (formatToScan.getDefaultLocationsToSearch()); | FileSearchPath path (formatToScan.getDefaultLocationsToSearch()); | ||||
| @@ -528,7 +545,9 @@ private: | |||||
| void PluginListComponent::scanFor (AudioPluginFormat& format) | 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 | bool PluginListComponent::isScanning() const noexcept | ||||
| @@ -55,6 +55,10 @@ public: | |||||
| /** Changes the text in the panel's options button. */ | /** Changes the text in the panel's options button. */ | ||||
| void setOptionsButtonText (const String& newText); | 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. | /** 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) | 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. */ | /** Returns true if there's currently a scan in progress. */ | ||||
| bool isScanning() const noexcept; | 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: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| AudioPluginFormatManager& formatManager; | AudioPluginFormatManager& formatManager; | ||||
| @@ -80,12 +95,11 @@ private: | |||||
| TableListBox table; | TableListBox table; | ||||
| TextButton optionsButton; | TextButton optionsButton; | ||||
| PropertiesFile* propertiesToUse; | PropertiesFile* propertiesToUse; | ||||
| String dialogTitle, dialogText; | |||||
| int numThreads; | int numThreads; | ||||
| class TableModel; | class TableModel; | ||||
| friend class TableModel; | |||||
| friend struct ContainerDeletePolicy<TableModel>; | |||||
| ScopedPointer<TableModel> tableModel; | |||||
| ScopedPointer<TableListBoxModel> tableModel; | |||||
| class Scanner; | class Scanner; | ||||
| friend class Scanner; | friend class Scanner; | ||||
| @@ -98,8 +112,8 @@ private: | |||||
| void updateList(); | void updateList(); | ||||
| void showSelectedFolder(); | void showSelectedFolder(); | ||||
| bool canShowSelectedFolder() const; | bool canShowSelectedFolder() const; | ||||
| void removeSelected(); | |||||
| void removeMissingPlugins(); | void removeMissingPlugins(); | ||||
| void removePluginItem (int index); | |||||
| void resized() override; | void resized() override; | ||||
| bool isInterestedInFileDrag (const StringArray&) override; | bool isInterestedInFileDrag (const StringArray&) override; | ||||
| @@ -69,7 +69,7 @@ | |||||
| void readFromFifo (int* someData, int numItems) | void readFromFifo (int* someData, int numItems) | ||||
| { | { | ||||
| int start1, size1, start2, size2; | int start1, size1, start2, size2; | ||||
| abstractFifo.prepareToRead (numSamples, start1, size1, start2, size2); | |||||
| abstractFifo.prepareToRead (numItems, start1, size1, start2, size2); | |||||
| if (size1 > 0) | if (size1 > 0) | ||||
| copySomeData (someData, myBuffer + start1, size1); | copySomeData (someData, myBuffer + start1, size1); | ||||
| @@ -832,8 +832,8 @@ public: | |||||
| /** Removes an item from the array. | /** 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 | @param valueToRemove the object to try to remove | ||||
| @see remove, removeRange | @see remove, removeRange | ||||
| @@ -119,26 +119,30 @@ public: | |||||
| */ | */ | ||||
| ~ReferenceCountedArray() | ~ReferenceCountedArray() | ||||
| { | { | ||||
| clear(); | |||||
| releaseAllObjects(); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| /** Removes all objects from the array. | /** 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() | void clear() | ||||
| { | { | ||||
| const ScopedLockType lock (getLock()); | const ScopedLockType lock (getLock()); | ||||
| while (numUsed > 0) | |||||
| if (ObjectClass* o = data.elements [--numUsed]) | |||||
| releaseObject (o); | |||||
| jassert (numUsed == 0); | |||||
| releaseAllObjects(); | |||||
| data.setAllocatedSize (0); | 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. */ | /** Returns the current number of objects in the array. */ | ||||
| inline int size() const noexcept | inline int size() const noexcept | ||||
| { | { | ||||
| @@ -886,6 +890,15 @@ private: | |||||
| ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse> data; | ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse> data; | ||||
| int numUsed; | int numUsed; | ||||
| void releaseAllObjects() | |||||
| { | |||||
| while (numUsed > 0) | |||||
| if (ObjectClass* o = data.elements [--numUsed]) | |||||
| releaseObject (o); | |||||
| jassert (numUsed == 0); | |||||
| } | |||||
| static void releaseObject (ObjectClass* o) | static void releaseObject (ObjectClass* o) | ||||
| { | { | ||||
| if (o->decReferenceCountWithoutDeleting()) | if (o->decReferenceCountWithoutDeleting()) | ||||
| @@ -57,7 +57,7 @@ int FileInputStream::read (void* buffer, int bytesToRead) | |||||
| jassert (buffer != nullptr && bytesToRead >= 0); | jassert (buffer != nullptr && bytesToRead >= 0); | ||||
| const size_t num = readInternal (buffer, (size_t) bytesToRead); | const size_t num = readInternal (buffer, (size_t) bytesToRead); | ||||
| currentPosition += num; | |||||
| currentPosition += (int64) num; | |||||
| return (int) num; | return (int) num; | ||||
| } | } | ||||
| @@ -90,7 +90,7 @@ bool FileOutputStream::write (const void* const src, const size_t numBytes) | |||||
| { | { | ||||
| memcpy (buffer + bytesInBuffer, src, numBytes); | memcpy (buffer + bytesInBuffer, src, numBytes); | ||||
| bytesInBuffer += numBytes; | bytesInBuffer += numBytes; | ||||
| currentPosition += numBytes; | |||||
| currentPosition += (int64) numBytes; | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -101,7 +101,7 @@ bool FileOutputStream::write (const void* const src, const size_t numBytes) | |||||
| { | { | ||||
| memcpy (buffer + bytesInBuffer, src, numBytes); | memcpy (buffer + bytesInBuffer, src, numBytes); | ||||
| bytesInBuffer += numBytes; | bytesInBuffer += numBytes; | ||||
| currentPosition += numBytes; | |||||
| currentPosition += (int64) numBytes; | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -110,7 +110,7 @@ bool FileOutputStream::write (const void* const src, const size_t numBytes) | |||||
| if (bytesWritten < 0) | if (bytesWritten < 0) | ||||
| return false; | return false; | ||||
| currentPosition += bytesWritten; | |||||
| currentPosition += (int64) bytesWritten; | |||||
| return bytesWritten == (ssize_t) numBytes; | return bytesWritten == (ssize_t) numBytes; | ||||
| } | } | ||||
| } | } | ||||
| @@ -126,7 +126,7 @@ bool FileOutputStream::writeRepeatedByte (uint8 byte, size_t numBytes) | |||||
| { | { | ||||
| memset (buffer + bytesInBuffer, byte, numBytes); | memset (buffer + bytesInBuffer, byte, numBytes); | ||||
| bytesInBuffer += numBytes; | bytesInBuffer += numBytes; | ||||
| currentPosition += numBytes; | |||||
| currentPosition += (int64) numBytes; | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -579,7 +579,7 @@ public: | |||||
| case 1: return r.nextInt(); | case 1: return r.nextInt(); | ||||
| case 2: return r.nextInt64(); | case 2: return r.nextInt64(); | ||||
| case 3: return r.nextBool(); | case 3: return r.nextBool(); | ||||
| case 4: return r.nextDouble(); | |||||
| case 4: return String (r.nextDouble(), 8).getDoubleValue(); | |||||
| case 5: return createRandomWideCharString (r); | case 5: return createRandomWideCharString (r); | ||||
| case 6: | case 6: | ||||
| @@ -149,6 +149,10 @@ struct JavascriptEngine::RootObject : public DynamicObject | |||||
| if (const var* prop = getPropertyPointer (p, functionName)) | if (const var* prop = getPropertyPointer (p, functionName)) | ||||
| return *prop; | 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()) | if (targetObject.isString()) | ||||
| @@ -699,6 +703,11 @@ struct JavascriptEngine::RootObject : public DynamicObject | |||||
| if (FunctionObject* fo = dynamic_cast<FunctionObject*> (function.getObject())) | if (FunctionObject* fo = dynamic_cast<FunctionObject*> (function.getObject())) | ||||
| return fo->invoke (s, args); | 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(); | location.throwError ("This expression is not a function!"); return var(); | ||||
| } | } | ||||
| @@ -107,6 +107,11 @@ | |||||
| #include <android/log.h> | #include <android/log.h> | ||||
| #endif | #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 | namespace juce | ||||
| @@ -274,6 +274,19 @@ inline void swapVariables (Type& variable1, Type& variable2) | |||||
| std::swap (variable1, 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. | /** Handy function for getting the number of elements in a simple const C array. | ||||
| E.g. | E.g. | ||||
| @code | @code | ||||
| @@ -299,11 +312,23 @@ template <typename Type> | |||||
| inline Type juce_hypot (Type a, Type b) noexcept | inline Type juce_hypot (Type a, Type b) noexcept | ||||
| { | { | ||||
| #if JUCE_MSVC | #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 | #else | ||||
| return static_cast <Type> (hypot (a, b)); | |||||
| return (hypotf (a, b)); | |||||
| #endif | #endif | ||||
| } | } | ||||
| #endif | |||||
| /** 64-bit abs function. */ | /** 64-bit abs function. */ | ||||
| inline int64 abs64 (const int64 n) noexcept | 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 | /** The isfinite() method seems to vary between platforms, so this is a | ||||
| platform-independent function for it. | 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 | #else | ||||
| return std::isfinite (value); | return std::isfinite (value); | ||||
| #endif | #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 | /** This macro can be applied to a float variable to check whether it contains a denormalised | ||||
| value, and to normalise it if necessary. | value, and to normalise it if necessary. | ||||
| On CPUs that aren't vulnerable to denormalisation problems, this will have no effect. | 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 | #else | ||||
| #define JUCE_UNDENORMALISE(x) | #define JUCE_UNDENORMALISE(x) | ||||
| #endif | #endif | ||||
| @@ -153,7 +153,7 @@ inline uint64 ByteOrder::swap (uint64 value) noexcept | |||||
| #elif JUCE_USE_MSVC_INTRINSICS | #elif JUCE_USE_MSVC_INTRINSICS | ||||
| return _byteswap_uint64 (value); | return _byteswap_uint64 (value); | ||||
| #else | #else | ||||
| return (((int64) swap ((uint32) value)) << 32) | swap ((uint32) (value >> 32)); | |||||
| return (((uint64) swap ((uint32) value)) << 32) | swap ((uint32) (value >> 32)); | |||||
| #endif | #endif | ||||
| } | } | ||||
| @@ -36,7 +36,7 @@ namespace HeapBlockHelper | |||||
| struct ThrowOnFail { static void check (void*) {} }; | struct ThrowOnFail { static void check (void*) {} }; | ||||
| template<> | 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 | #endif | ||||
| @@ -67,7 +67,7 @@ namespace HeapBlockHelper | |||||
| ..you could just write this: | ..you could just write this: | ||||
| @code | @code | ||||
| HeapBlock <int> temp (1024); | |||||
| HeapBlock<int> temp (1024); | |||||
| memcpy (temp, xyz, 1024 * sizeof (int)); | memcpy (temp, xyz, 1024 * sizeof (int)); | ||||
| temp.calloc (2048); | temp.calloc (2048); | ||||
| temp[0] = 1234; | temp[0] = 1234; | ||||
| @@ -109,7 +109,7 @@ public: | |||||
| other constructor that takes an InitialisationState parameter. | other constructor that takes an InitialisationState parameter. | ||||
| */ | */ | ||||
| explicit HeapBlock (const size_t numElements) | 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(); | throwOnAllocationFailure(); | ||||
| } | } | ||||
| @@ -120,7 +120,7 @@ public: | |||||
| or left uninitialised. | or left uninitialised. | ||||
| */ | */ | ||||
| HeapBlock (const size_t numElements, const bool initialiseToZero) | HeapBlock (const size_t numElements, const bool initialiseToZero) | ||||
| : data (static_cast <ElementType*> (initialiseToZero | |||||
| : data (static_cast<ElementType*> (initialiseToZero | |||||
| ? std::calloc (numElements, sizeof (ElementType)) | ? std::calloc (numElements, sizeof (ElementType)) | ||||
| : std::malloc (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 | 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. | 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. | /** 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 | 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. | 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. | /** 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 | 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)) | void malloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType)) | ||||
| { | { | ||||
| std::free (data); | std::free (data); | ||||
| data = static_cast <ElementType*> (std::malloc (newNumElements * elementSize)); | |||||
| data = static_cast<ElementType*> (std::malloc (newNumElements * elementSize)); | |||||
| throwOnAllocationFailure(); | throwOnAllocationFailure(); | ||||
| } | } | ||||
| @@ -230,7 +230,7 @@ public: | |||||
| void calloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType)) | void calloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType)) | ||||
| { | { | ||||
| std::free (data); | std::free (data); | ||||
| data = static_cast <ElementType*> (std::calloc (newNumElements, elementSize)); | |||||
| data = static_cast<ElementType*> (std::calloc (newNumElements, elementSize)); | |||||
| throwOnAllocationFailure(); | throwOnAllocationFailure(); | ||||
| } | } | ||||
| @@ -241,7 +241,7 @@ public: | |||||
| void allocate (const size_t newNumElements, bool initialiseToZero) | void allocate (const size_t newNumElements, bool initialiseToZero) | ||||
| { | { | ||||
| std::free (data); | std::free (data); | ||||
| data = static_cast <ElementType*> (initialiseToZero | |||||
| data = static_cast<ElementType*> (initialiseToZero | |||||
| ? std::calloc (newNumElements, sizeof (ElementType)) | ? std::calloc (newNumElements, sizeof (ElementType)) | ||||
| : std::malloc (newNumElements * sizeof (ElementType))); | : std::malloc (newNumElements * sizeof (ElementType))); | ||||
| throwOnAllocationFailure(); | throwOnAllocationFailure(); | ||||
| @@ -254,15 +254,15 @@ public: | |||||
| */ | */ | ||||
| void realloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType)) | 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(); | throwOnAllocationFailure(); | ||||
| } | } | ||||
| /** Frees any currently-allocated data. | /** Frees any currently-allocated data. | ||||
| This will free the data and reset this object to be a null pointer. | This will free the data and reset this object to be a null pointer. | ||||
| */ | */ | ||||
| void free() | |||||
| void free() noexcept | |||||
| { | { | ||||
| std::free (data); | std::free (data); | ||||
| data = nullptr; | data = nullptr; | ||||
| @@ -272,7 +272,7 @@ public: | |||||
| The two objects simply exchange their data pointers. | The two objects simply exchange their data pointers. | ||||
| */ | */ | ||||
| template <bool otherBlockThrows> | template <bool otherBlockThrows> | ||||
| void swapWith (HeapBlock <ElementType, otherBlockThrows>& other) noexcept | |||||
| void swapWith (HeapBlock<ElementType, otherBlockThrows>& other) noexcept | |||||
| { | { | ||||
| std::swap (data, other.data); | 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) | 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); | zeromem (d + newNum, num - newNum); | ||||
| num = newNum; | num = newNum; | ||||
| } | } | ||||
| @@ -44,7 +44,7 @@ | |||||
| destructor, in case it is deleted by other means than deleteInstance() | destructor, in case it is deleted by other means than deleteInstance() | ||||
| Clients can then call the static method MyClass::getInstance() to get a pointer | 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. | no instance currently exists. | ||||
| e.g. @code | e.g. @code | ||||
| @@ -331,15 +331,26 @@ public class JuceAppActivity extends Activity | |||||
| setFocusableInTouchMode (true); | setFocusableInTouchMode (true); | ||||
| setOnFocusChangeListener (this); | setOnFocusChangeListener (this); | ||||
| requestFocus(); | 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 | @Override | ||||
| public void onDraw (Canvas canvas) | public void onDraw (Canvas canvas) | ||||
| { | { | ||||
| handlePaint (host, canvas); | |||||
| handlePaint (host, canvas, paint); | |||||
| } | } | ||||
| @Override | @Override | ||||
| @@ -350,6 +361,7 @@ public class JuceAppActivity extends Activity | |||||
| private boolean opaque; | private boolean opaque; | ||||
| private long host; | private long host; | ||||
| private Paint paint = new Paint(); | |||||
| //============================================================================== | //============================================================================== | ||||
| private native void handleMouseDown (long host, int index, float x, float y, long time); | 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; | 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 | // this is here to make keyboard entry work on a Galaxy Tab2 10.1 | ||||
| @Override | @Override | ||||
| public InputConnection onCreateInputConnection (EditorInfo outAttrs) | public InputConnection onCreateInputConnection (EditorInfo outAttrs) | ||||
| @@ -138,8 +138,8 @@ private: | |||||
| JUCE_DECLARE_NON_COPYABLE (Pimpl) | 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 currentExecutableFile: | ||||
| case currentApplicationFile: | case currentApplicationFile: | ||||
| #if ! JUCE_STANDALONE_APPLICATION | |||||
| return juce_getExecutableFile(); | return juce_getExecutableFile(); | ||||
| #endif | |||||
| // deliberate fall-through if this is not a shared-library | |||||
| case hostApplicationPath: | case hostApplicationPath: | ||||
| { | { | ||||
| @@ -216,7 +219,7 @@ bool Process::openDocument (const String& fileName, const String& parameters) | |||||
| cmdString = cmdLines.joinIntoString (" || "); | 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 | #if JUCE_USE_VFORK | ||||
| const int cpid = vfork(); | const int cpid = vfork(); | ||||
| @@ -226,11 +229,12 @@ bool Process::openDocument (const String& fileName, const String& parameters) | |||||
| if (cpid == 0) | if (cpid == 0) | ||||
| { | { | ||||
| #if ! JUCE_USE_VFORK | |||||
| setsid(); | setsid(); | ||||
| #endif | |||||
| // Child process | // Child process | ||||
| execve (argv[0], (char**) argv, environ); | |||||
| exit (0); | |||||
| if (execvp (argv[0], (char**) argv) < 0) | |||||
| _exit (0); | |||||
| } | } | ||||
| return cpid >= 0; | return cpid >= 0; | ||||
| @@ -127,7 +127,7 @@ public: | |||||
| if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) | if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) | ||||
| return 0; // (timeout) | 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) | if (bytesRead == 0) | ||||
| finished = true; | finished = true; | ||||
| @@ -182,7 +182,7 @@ private: | |||||
| } | } | ||||
| int createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | int createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | ||||
| const int numRedirectsToFollow) | |||||
| const int numRedirects) | |||||
| { | { | ||||
| closeSocket (false); | closeSocket (false); | ||||
| @@ -193,7 +193,7 @@ private: | |||||
| else if (timeOutMs < 0) | else if (timeOutMs < 0) | ||||
| timeOutTime = 0xffffffff; | timeOutTime = 0xffffffff; | ||||
| else | else | ||||
| timeOutTime += timeOutMs; | |||||
| timeOutTime += (uint32) timeOutMs; | |||||
| String hostName, hostPath; | String hostName, hostPath; | ||||
| int hostPort; | int hostPort; | ||||
| @@ -279,7 +279,7 @@ private: | |||||
| String location (findHeaderItem (headerLines, "Location:")); | String location (findHeaderItem (headerLines, "Location:")); | ||||
| if (++levelsOfRedirection <= numRedirectsToFollow | |||||
| if (++levelsOfRedirection <= numRedirects | |||||
| && status >= 300 && status < 400 | && status >= 300 && status < 400 | ||||
| && location.isNotEmpty() && location != address) | && location.isNotEmpty() && location != address) | ||||
| { | { | ||||
| @@ -295,7 +295,7 @@ private: | |||||
| } | } | ||||
| address = location; | address = location; | ||||
| return createConnection (progressCallback, progressCallbackContext, numRedirectsToFollow); | |||||
| return createConnection (progressCallback, progressCallbackContext, numRedirects); | |||||
| } | } | ||||
| return status; | return status; | ||||
| @@ -391,12 +391,12 @@ private: | |||||
| const int numToSend = jmin (1024, (int) (requestHeader.getSize() - totalHeaderSent)); | 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; | 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; | return false; | ||||
| } | } | ||||
| @@ -55,10 +55,10 @@ bool SystemStats::isOperatingSystem64Bit() | |||||
| //============================================================================== | //============================================================================== | ||||
| namespace LinuxStatsHelpers | namespace LinuxStatsHelpers | ||||
| { | { | ||||
| String getCpuInfo (const char* const key) | |||||
| String getConfigFileValue (const char* file, const char* const key) | |||||
| { | { | ||||
| StringArray lines; | 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) | 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)) | if (lines[i].upToFirstOccurrenceOf (":", false, false).trim().equalsIgnoreCase (key)) | ||||
| @@ -66,6 +66,11 @@ namespace LinuxStatsHelpers | |||||
| return String(); | return String(); | ||||
| } | } | ||||
| String getCpuInfo (const char* key) | |||||
| { | |||||
| return getConfigFileValue ("/proc/cpuinfo", key); | |||||
| } | |||||
| } | } | ||||
| String SystemStats::getDeviceDescription() | String SystemStats::getDeviceDescription() | ||||
| @@ -93,14 +98,14 @@ int SystemStats::getMemorySizeInMegabytes() | |||||
| struct sysinfo sysi; | struct sysinfo sysi; | ||||
| if (sysinfo (&sysi) == 0) | if (sysinfo (&sysi) == 0) | ||||
| return sysi.totalram * sysi.mem_unit / (1024 * 1024); | |||||
| return (int) (sysi.totalram * sysi.mem_unit / (1024 * 1024)); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| int SystemStats::getPageSize() | int SystemStats::getPageSize() | ||||
| { | { | ||||
| return sysconf (_SC_PAGESIZE); | |||||
| return (int) sysconf (_SC_PAGESIZE); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -150,6 +155,8 @@ void CPUInformation::initialise() noexcept | |||||
| hasSSE2 = flags.contains ("sse2"); | hasSSE2 = flags.contains ("sse2"); | ||||
| hasSSE3 = flags.contains ("sse3"); | hasSSE3 = flags.contains ("sse3"); | ||||
| has3DNow = flags.contains ("3dnow"); | has3DNow = flags.contains ("3dnow"); | ||||
| hasSSSE3 = flags.contains ("ssse3"); | |||||
| hasAVX = flags.contains ("avx"); | |||||
| numCpus = LinuxStatsHelpers::getCpuInfo ("processor").getIntValue() + 1; | numCpus = LinuxStatsHelpers::getCpuInfo ("processor").getIntValue() + 1; | ||||
| } | } | ||||
| @@ -160,7 +167,7 @@ uint32 juce_millisecondsSinceStartup() noexcept | |||||
| timespec t; | timespec t; | ||||
| clock_gettime (CLOCK_MONOTONIC, &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 | int64 Time::getHighResolutionTicks() noexcept | ||||
| @@ -189,3 +196,13 @@ bool Time::setSystemTimeToThisTime() const | |||||
| return settimeofday (&t, 0) == 0; | 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); | 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() | JUCE_API bool JUCE_CALLTYPE Process::isRunningUnderDebugger() | ||||
| { | { | ||||
| return juce_isRunningUnderDebugger(); | return juce_isRunningUnderDebugger(); | ||||
| @@ -110,7 +110,7 @@ namespace FileHelpers | |||||
| static bool launchExecutable (const String& pathAndArguments) | 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 | #if JUCE_USE_VFORK | ||||
| const int cpid = vfork(); | const int cpid = vfork(); | ||||
| @@ -121,16 +121,11 @@ namespace FileHelpers | |||||
| if (cpid == 0) | if (cpid == 0) | ||||
| { | { | ||||
| // Child process | // 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), | hasFailed (false), | ||||
| hasFinished (false), | hasFinished (false), | ||||
| numRedirectsToFollow (maxRedirects), | numRedirectsToFollow (maxRedirects), | ||||
| numRedirects (0) | |||||
| numRedirects (0), | |||||
| latestTotalBytes (0) | |||||
| { | { | ||||
| static DelegateClass cls; | static DelegateClass cls; | ||||
| delegate = [cls.createInstance() init]; | delegate = [cls.createInstance() init]; | ||||
| @@ -152,7 +153,7 @@ public: | |||||
| while (isThreadRunning() && ! initialised) | while (isThreadRunning() && ! initialised) | ||||
| { | { | ||||
| if (callback != nullptr) | if (callback != nullptr) | ||||
| callback (context, -1, (int) [[request HTTPBody] length]); | |||||
| callback (context, latestTotalBytes, (int) [[request HTTPBody] length]); | |||||
| Thread::sleep (1); | Thread::sleep (1); | ||||
| } | } | ||||
| @@ -203,7 +204,6 @@ public: | |||||
| [data setLength: 0]; | [data setLength: 0]; | ||||
| } | } | ||||
| initialised = true; | |||||
| contentLength = [response expectedContentLength]; | contentLength = [response expectedContentLength]; | ||||
| [headers release]; | [headers release]; | ||||
| @@ -215,6 +215,8 @@ public: | |||||
| headers = [[httpResponse allHeaderFields] retain]; | headers = [[httpResponse allHeaderFields] retain]; | ||||
| statusCode = (int) [httpResponse statusCode]; | statusCode = (int) [httpResponse statusCode]; | ||||
| } | } | ||||
| initialised = true; | |||||
| } | } | ||||
| NSURLRequest* willSendRequest (NSURLRequest* newRequest, NSURLResponse* redirectResponse) | NSURLRequest* willSendRequest (NSURLRequest* newRequest, NSURLResponse* redirectResponse) | ||||
| @@ -245,8 +247,9 @@ public: | |||||
| initialised = true; | initialised = true; | ||||
| } | } | ||||
| void didSendBodyData (NSInteger /*totalBytesWritten*/, NSInteger /*totalBytesExpected*/) | |||||
| void didSendBodyData (NSInteger totalBytesWritten, NSInteger /*totalBytesExpected*/) | |||||
| { | { | ||||
| latestTotalBytes = static_cast<int> (totalBytesWritten); | |||||
| } | } | ||||
| void finishedLoading() | void finishedLoading() | ||||
| @@ -280,6 +283,7 @@ public: | |||||
| bool initialised, hasFailed, hasFinished; | bool initialised, hasFailed, hasFinished; | ||||
| const int numRedirectsToFollow; | const int numRedirectsToFollow; | ||||
| int numRedirects; | int numRedirects; | ||||
| int latestTotalBytes; | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -80,6 +80,8 @@ void CPUInformation::initialise() noexcept | |||||
| hasSSE2 = (d & (1u << 26)) != 0; | hasSSE2 = (d & (1u << 26)) != 0; | ||||
| has3DNow = (b & (1u << 31)) != 0; | has3DNow = (b & (1u << 31)) != 0; | ||||
| hasSSE3 = (c & (1u << 0)) != 0; | hasSSE3 = (c & (1u << 0)) != 0; | ||||
| hasSSSE3 = (c & (1u << 9)) != 0; | |||||
| hasAVX = (c & (1u << 28)) != 0; | |||||
| #endif | #endif | ||||
| #if JUCE_IOS || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) | #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.getWidth()), | ||||
| static_cast <CGFloat> (r.getHeight())); | static_cast <CGFloat> (r.getHeight())); | ||||
| } | } | ||||
| #endif | |||||
| // These hacks are a workaround for newer Xcode builds which by default prevent calls to these objc functions.. | // 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, ...); | typedef id (*MsgSendSuperFn) (struct objc_super*, SEL, ...); | ||||
| static inline MsgSendSuperFn getMsgSendSuperFn() noexcept { return (MsgSendSuperFn) (void*) objc_msgSendSuper; } | static inline MsgSendSuperFn getMsgSendSuperFn() noexcept { return (MsgSendSuperFn) (void*) objc_msgSendSuper; } | ||||
| #if ! JUCE_PPC | |||||
| typedef double (*MsgSendFPRetFn) (id, SEL op, ...); | typedef double (*MsgSendFPRetFn) (id, SEL op, ...); | ||||
| static inline MsgSendFPRetFn getMsgSendFPRetFn() noexcept { return (MsgSendFPRetFn) (void*) objc_msgSend_fpret; } | static inline MsgSendFPRetFn getMsgSendFPRetFn() noexcept { return (MsgSendFPRetFn) (void*) objc_msgSend_fpret; } | ||||
| #endif | |||||
| #endif | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -147,11 +149,13 @@ struct ObjCClass | |||||
| jassert (b); (void) b; | jassert (b); (void) b; | ||||
| } | } | ||||
| #if JUCE_MAC | |||||
| static id sendSuperclassMessage (id self, SEL selector) | static id sendSuperclassMessage (id self, SEL selector) | ||||
| { | { | ||||
| objc_super s = { self, [SuperclassType class] }; | objc_super s = { self, [SuperclassType class] }; | ||||
| return getMsgSendSuperFn() (&s, selector); | return getMsgSendSuperFn() (&s, selector); | ||||
| } | } | ||||
| #endif | |||||
| template <typename Type> | template <typename Type> | ||||
| static Type getIvar (id self, const char* name) | static Type getIvar (id self, const char* name) | ||||
| @@ -596,12 +596,38 @@ File juce_getExecutableFile() | |||||
| { | { | ||||
| Dl_info exeInfo; | Dl_info exeInfo; | ||||
| dladdr ((void*) juce_getExecutableFile, &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()); | static String filename (DLAddrReader::getFilename()); | ||||
| return File::getCurrentWorkingDirectory().getChildFile (filename); | |||||
| return filename; | |||||
| #endif | #endif | ||||
| } | } | ||||
| @@ -961,22 +987,22 @@ void JUCE_CALLTYPE Thread::setCurrentThreadAffinityMask (const uint32 affinityMa | |||||
| if ((affinityMask & (1 << i)) != 0) | if ((affinityMask & (1 << i)) != 0) | ||||
| CPU_SET (i, &affinity); | 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); | sched_setaffinity (getpid(), sizeof (cpu_set_t), &affinity); | ||||
| #endif | |||||
| sched_yield(); | sched_yield(); | ||||
| #else | #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; | jassertfalse; | ||||
| (void) affinityMask; | |||||
| ignoreUnused (affinityMask); | |||||
| #endif | #endif | ||||
| } | } | ||||
| @@ -1057,8 +1083,8 @@ public: | |||||
| close (pipeHandles[1]); | close (pipeHandles[1]); | ||||
| #endif | #endif | ||||
| execvp (argv[0], argv.getRawDataPointer()); | |||||
| exit (-1); | |||||
| if (execvp (argv[0], argv.getRawDataPointer()) < 0) | |||||
| _exit (-1); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -1285,7 +1311,7 @@ private: | |||||
| { | { | ||||
| struct timespec t; | struct timespec t; | ||||
| clock_gettime (CLOCK_MONOTONIC, &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 | void wait() noexcept | ||||
| @@ -105,6 +105,8 @@ void CPUInformation::initialise() noexcept | |||||
| hasSSE = (info[3] & (1 << 25)) != 0; | hasSSE = (info[3] & (1 << 25)) != 0; | ||||
| hasSSE2 = (info[3] & (1 << 26)) != 0; | hasSSE2 = (info[3] & (1 << 26)) != 0; | ||||
| hasSSE3 = (info[2] & (1 << 0)) != 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; | has3DNow = (info[1] & (1 << 31)) != 0; | ||||
| SYSTEM_INFO systemInfo; | SYSTEM_INFO systemInfo; | ||||
| @@ -131,7 +133,12 @@ static bool isWindowsVersionOrLater (SystemStats::OperatingSystemType target) | |||||
| zerostruct (info); | zerostruct (info); | ||||
| info.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX); | 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; | info.dwMajorVersion = 6; | ||||
| @@ -166,7 +173,7 @@ static bool isWindowsVersionOrLater (SystemStats::OperatingSystemType target) | |||||
| SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() | SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() | ||||
| { | { | ||||
| const SystemStats::OperatingSystemType types[] | 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) | for (int i = 0; i < numElementsInArray (types); ++i) | ||||
| if (isWindowsVersionOrLater (types[i])) | if (isWindowsVersionOrLater (types[i])) | ||||
| @@ -182,6 +189,7 @@ String SystemStats::getOperatingSystemName() | |||||
| switch (getOperatingSystemType()) | switch (getOperatingSystemType()) | ||||
| { | { | ||||
| case Windows10: name = "Windows 10"; break; | |||||
| case Windows8_1: name = "Windows 8.1"; break; | case Windows8_1: name = "Windows 8.1"; break; | ||||
| case Windows8_0: name = "Windows 8.0"; break; | case Windows8_0: name = "Windows 8.0"; break; | ||||
| case Windows7: name = "Windows 7"; 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; | cfg.ifc_buf += IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len; | ||||
| } | } | ||||
| #else | #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]; | 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)); | : (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; | return false; | ||||
| struct sockaddr_in servTmpAddr; | struct sockaddr_in servTmpAddr; | ||||
| @@ -86,30 +136,64 @@ namespace SocketHelpers | |||||
| servTmpAddr.sin_addr.s_addr = htonl (INADDR_ANY); | servTmpAddr.sin_addr.s_addr = htonl (INADDR_ANY); | ||||
| servTmpAddr.sin_port = htons ((uint16) port); | 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; | 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, | static int readSocket (const SocketHandle handle, | ||||
| void* const destBuffer, const int maxBytesToRead, | void* const destBuffer, const int maxBytesToRead, | ||||
| bool volatile& connected, | bool volatile& connected, | ||||
| const bool blockUntilSpecifiedAmountHasArrived) noexcept | |||||
| const bool blockUntilSpecifiedAmountHasArrived, | |||||
| CriticalSection& readLock, | |||||
| String* senderIP = nullptr, | |||||
| int* senderPort = nullptr) noexcept | |||||
| { | { | ||||
| int bytesRead = 0; | int bytesRead = 0; | ||||
| while (bytesRead < maxBytesToRead) | 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) | if (bytesThisTime <= 0 || ! connected) | ||||
| { | { | ||||
| @@ -125,11 +209,20 @@ namespace SocketHelpers | |||||
| break; | 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 timeout; | ||||
| struct timeval* timeoutp; | struct timeval* timeoutp; | ||||
| @@ -146,20 +239,20 @@ namespace SocketHelpers | |||||
| fd_set rset, wset; | fd_set rset, wset; | ||||
| FD_ZERO (&rset); | FD_ZERO (&rset); | ||||
| FD_SET (handle, &rset); | |||||
| FD_SET (h, &rset); | |||||
| FD_ZERO (&wset); | FD_ZERO (&wset); | ||||
| FD_SET (handle, &wset); | |||||
| FD_SET (h, &wset); | |||||
| fd_set* const prset = forReading ? &rset : nullptr; | fd_set* const prset = forReading ? &rset : nullptr; | ||||
| fd_set* const pwset = forReading ? nullptr : &wset; | fd_set* const pwset = forReading ? nullptr : &wset; | ||||
| #if JUCE_WINDOWS | #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; | return -1; | ||||
| #else | #else | ||||
| { | { | ||||
| int result; | int result; | ||||
| while ((result = select (handle + 1, prset, pwset, 0, timeoutp)) < 0 | |||||
| while ((result = select (h + 1, prset, pwset, 0, timeoutp)) < 0 | |||||
| && errno == EINTR) | && errno == EINTR) | ||||
| { | { | ||||
| } | } | ||||
| @@ -169,16 +262,20 @@ namespace SocketHelpers | |||||
| } | } | ||||
| #endif | #endif | ||||
| // we are closing | |||||
| if (handle < 0) | |||||
| return -1; | |||||
| { | { | ||||
| int opt; | int opt; | ||||
| juce_socklen_t len = sizeof (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) | || opt != 0) | ||||
| return -1; | 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 | static bool setSocketBlockingState (const SocketHandle handle, const bool shouldBlock) noexcept | ||||
| @@ -201,12 +298,7 @@ namespace SocketHelpers | |||||
| #endif | #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; | struct addrinfo hints; | ||||
| zerostruct (hints); | zerostruct (hints); | ||||
| @@ -216,8 +308,22 @@ namespace SocketHelpers | |||||
| hints.ai_flags = AI_NUMERICSERV; | hints.ai_flags = AI_NUMERICSERV; | ||||
| struct addrinfo* info = nullptr; | 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; | return false; | ||||
| if (handle < 0) | if (handle < 0) | ||||
| @@ -229,19 +335,12 @@ namespace SocketHelpers | |||||
| return false; | return false; | ||||
| } | } | ||||
| if (isDatagram) | |||||
| { | |||||
| if (*serverAddress != nullptr) | |||||
| freeaddrinfo (*serverAddress); | |||||
| *serverAddress = info; | |||||
| return true; | |||||
| } | |||||
| setSocketBlockingState (handle, false); | setSocketBlockingState (handle, false); | ||||
| const int result = ::connect (handle, info->ai_addr, (socklen_t) info->ai_addrlen); | const int result = ::connect (handle, info->ai_addr, (socklen_t) info->ai_addrlen); | ||||
| freeaddrinfo (info); | freeaddrinfo (info); | ||||
| bool retval = (result >= 0); | |||||
| if (result < 0) | if (result < 0) | ||||
| { | { | ||||
| #if JUCE_WINDOWS | #if JUCE_WINDOWS | ||||
| @@ -250,18 +349,17 @@ namespace SocketHelpers | |||||
| if (errno == EINPROGRESS) | if (errno == EINPROGRESS) | ||||
| #endif | #endif | ||||
| { | { | ||||
| if (waitForReadiness (handle, false, timeOutMillisecs) != 1) | |||||
| { | |||||
| setSocketBlockingState (handle, true); | |||||
| return false; | |||||
| } | |||||
| if (waitForReadiness (handle, readLock, false, timeOutMillisecs) == 1) | |||||
| retval = true; | |||||
| } | } | ||||
| } | } | ||||
| setSocketBlockingState (handle, true); | setSocketBlockingState (handle, true); | ||||
| resetSocketOptions (handle, false, false); | |||||
| return true; | |||||
| if (retval) | |||||
| resetSocketOptions (handle, false, false); | |||||
| return retval; | |||||
| } | } | ||||
| static void makeReusable (int handle) noexcept | static void makeReusable (int handle) noexcept | ||||
| @@ -269,6 +367,23 @@ namespace SocketHelpers | |||||
| const int reuse = 1; | const int reuse = 1; | ||||
| setsockopt (handle, SOL_SOCKET, SO_REUSEADDR, (const char*) &reuse, sizeof (reuse)); | 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, | return (connected && ! isListener) ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, | ||||
| connected, blockUntilSpecifiedAmountHasArrived) | |||||
| connected, shouldBlock, readLock) | |||||
| : -1; | : -1; | ||||
| } | } | ||||
| @@ -311,32 +425,31 @@ int StreamingSocket::write (const void* sourceBuffer, const int numBytesToWrite) | |||||
| if (isListener || ! connected) | if (isListener || ! connected) | ||||
| return -1; | 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, | int StreamingSocket::waitUntilReady (const bool readyForReading, | ||||
| const int timeoutMsecs) const | const int timeoutMsecs) const | ||||
| { | { | ||||
| return connected ? SocketHelpers::waitForReadiness (handle, readyForReading, timeoutMsecs) | |||||
| return connected ? SocketHelpers::waitForReadiness (handle, readLock, readyForReading, timeoutMsecs) | |||||
| : -1; | : -1; | ||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| bool StreamingSocket::bindToPort (const int port) | 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, | bool StreamingSocket::connect (const String& remoteHostName, | ||||
| @@ -356,7 +469,7 @@ bool StreamingSocket::connect (const String& remoteHostName, | |||||
| portNumber = remotePortNumber; | portNumber = remotePortNumber; | ||||
| isListener = false; | isListener = false; | ||||
| connected = SocketHelpers::connectSocket (handle, false, nullptr, remoteHostName, | |||||
| connected = SocketHelpers::connectSocket (handle, readLock, remoteHostName, | |||||
| remotePortNumber, timeOutMillisecs); | remotePortNumber, timeOutMillisecs); | ||||
| if (! (connected && SocketHelpers::resetSocketOptions (handle, false, false))) | if (! (connected && SocketHelpers::resetSocketOptions (handle, false, false))) | ||||
| @@ -370,30 +483,7 @@ bool StreamingSocket::connect (const String& remoteHostName, | |||||
| void StreamingSocket::close() | 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(); | hostName.clear(); | ||||
| portNumber = 0; | 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(); | SocketHelpers::initSockets(); | ||||
| handle = (int) socket (AF_INET, SOCK_DGRAM, 0); | handle = (int) socket (AF_INET, SOCK_DGRAM, 0); | ||||
| SocketHelpers::resetSocketOptions (handle, true, canBroadcast); | |||||
| SocketHelpers::makeReusable (handle); | 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() | 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) | 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, | int DatagramSocket::waitUntilReady (const bool readyForReading, | ||||
| const int timeoutMsecs) const | 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 | #if JUCE_MSVC | ||||
| @@ -65,6 +65,25 @@ public: | |||||
| */ | */ | ||||
| bool bindToPort (int localPortNumber); | 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. | /** Tries to connect the socket to hostname:port. | ||||
| If timeOutMillisecs is 0, then this method will block until the operating system | If timeOutMillisecs is 0, then this method will block until the operating system | ||||
| @@ -164,6 +183,7 @@ private: | |||||
| String hostName; | String hostName; | ||||
| int volatile portNumber, handle; | int volatile portNumber, handle; | ||||
| bool connected, isListener; | bool connected, isListener; | ||||
| mutable CriticalSection readLock; | |||||
| StreamingSocket (const String& hostname, int portNumber, int handle); | StreamingSocket (const String& hostname, int portNumber, int handle); | ||||
| @@ -185,22 +205,16 @@ class JUCE_API DatagramSocket | |||||
| public: | 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 | If enableBroadcasting is true, the socket will be allowed to send broadcast messages | ||||
| (may require extra privileges on linux) | (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. */ | /** Destructor. */ | ||||
| ~DatagramSocket(); | ~DatagramSocket(); | ||||
| @@ -208,37 +222,33 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Binds the socket to the specified local port. | /** 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 | @returns true on success; false may indicate that another socket is already bound | ||||
| on the same port | on the same port | ||||
| */ | */ | ||||
| bool bindToPort (int localPortNumber); | 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. */ | /** Returns the OS's socket handle that's currently open. */ | ||||
| int getRawSocketHandle() const noexcept { return handle; } | int getRawSocketHandle() const noexcept { return handle; } | ||||
| @@ -271,6 +281,21 @@ public: | |||||
| int read (void* destBuffer, int maxBytesToRead, | int read (void* destBuffer, int maxBytesToRead, | ||||
| bool blockUntilSpecifiedAmountHasArrived); | 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. | /** Writes bytes to the socket from a buffer. | ||||
| Note that this method will block unless you have checked the socket is ready | 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. | @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: | 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) | 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 | capacity off the block, so that its length matches the amount of actual data that | ||||
| has been written so far. | has been written so far. | ||||
| */ | */ | ||||
| void flush(); | |||||
| void flush() override; | |||||
| bool write (const void*, size_t) override; | bool write (const void*, size_t) override; | ||||
| int64 getPosition() override { return (int64) position; } | int64 getPosition() override { return (int64) position; } | ||||
| @@ -67,7 +67,8 @@ struct CPUInformation | |||||
| { | { | ||||
| CPUInformation() noexcept | CPUInformation() noexcept | ||||
| : numCpus (0), hasMMX (false), hasSSE (false), | : numCpus (0), hasMMX (false), hasSSE (false), | ||||
| hasSSE2 (false), hasSSE3 (false), has3DNow (false) | |||||
| hasSSE2 (false), hasSSE3 (false), has3DNow (false), | |||||
| hasSSSE3 (false), hasAVX (false) | |||||
| { | { | ||||
| initialise(); | initialise(); | ||||
| } | } | ||||
| @@ -75,7 +76,7 @@ struct CPUInformation | |||||
| void initialise() noexcept; | void initialise() noexcept; | ||||
| int numCpus; | int numCpus; | ||||
| bool hasMMX, hasSSE, hasSSE2, hasSSE3, has3DNow; | |||||
| bool hasMMX, hasSSE, hasSSE2, hasSSE3, has3DNow, hasSSSE3, hasAVX; | |||||
| }; | }; | ||||
| static const CPUInformation& getCPUInformation() noexcept | static const CPUInformation& getCPUInformation() noexcept | ||||
| @@ -86,10 +87,12 @@ static const CPUInformation& getCPUInformation() noexcept | |||||
| int SystemStats::getNumCpus() noexcept { return getCPUInformation().numCpus; } | int SystemStats::getNumCpus() noexcept { return getCPUInformation().numCpus; } | ||||
| bool SystemStats::hasMMX() noexcept { return getCPUInformation().hasMMX; } | bool SystemStats::hasMMX() noexcept { return getCPUInformation().hasMMX; } | ||||
| bool SystemStats::has3DNow() noexcept { return getCPUInformation().has3DNow; } | |||||
| bool SystemStats::hasSSE() noexcept { return getCPUInformation().hasSSE; } | bool SystemStats::hasSSE() noexcept { return getCPUInformation().hasSSE; } | ||||
| bool SystemStats::hasSSE2() noexcept { return getCPUInformation().hasSSE2; } | bool SystemStats::hasSSE2() noexcept { return getCPUInformation().hasSSE2; } | ||||
| bool SystemStats::hasSSE3() noexcept { return getCPUInformation().hasSSE3; } | 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. */ | /** The set of possible results of the getOperatingSystemType() method. */ | ||||
| enum OperatingSystemType | 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. | /** Returns the type of operating system we're running on. | ||||
| @@ -150,10 +152,12 @@ public: | |||||
| static String getCpuVendor(); | static String getCpuVendor(); | ||||
| static bool hasMMX() noexcept; /**< Returns true if Intel MMX instructions are available. */ | static bool hasMMX() noexcept; /**< Returns true if Intel MMX instructions are available. */ | ||||
| static bool has3DNow() noexcept; /**< Returns true if AMD 3DNOW instructions are available. */ | |||||
| static bool hasSSE() noexcept; /**< Returns true if Intel SSE instructions are available. */ | static bool hasSSE() noexcept; /**< Returns true if Intel SSE instructions are available. */ | ||||
| static bool hasSSE2() noexcept; /**< Returns true if Intel SSE2 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 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. | /** Finds out how much RAM is in the machine. | ||||
| @@ -511,7 +511,7 @@ public: | |||||
| if (byte < 0) | if (byte < 0) | ||||
| { | { | ||||
| uint8 bit = 0x40; | |||||
| int bit = 0x40; | |||||
| int numExtraValues = 0; | int numExtraValues = 0; | ||||
| while ((byte & bit) != 0) | while ((byte & bit) != 0) | ||||
| @@ -34,18 +34,18 @@ | |||||
| juce_wchar CharacterFunctions::toUpperCase (const juce_wchar character) noexcept | 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 | 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 | bool CharacterFunctions::isUpperCase (const juce_wchar character) noexcept | ||||
| { | { | ||||
| #if JUCE_WINDOWS | #if JUCE_WINDOWS | ||||
| return iswupper ((wchar_t) character) != 0; | |||||
| return iswupper ((wint_t) character) != 0; | |||||
| #else | #else | ||||
| return toLowerCase (character) != character; | return toLowerCase (character) != character; | ||||
| #endif | #endif | ||||
| @@ -54,7 +54,7 @@ bool CharacterFunctions::isUpperCase (const juce_wchar character) noexcept | |||||
| bool CharacterFunctions::isLowerCase (const juce_wchar character) noexcept | bool CharacterFunctions::isLowerCase (const juce_wchar character) noexcept | ||||
| { | { | ||||
| #if JUCE_WINDOWS | #if JUCE_WINDOWS | ||||
| return iswlower ((wchar_t) character) != 0; | |||||
| return iswlower ((wint_t) character) != 0; | |||||
| #else | #else | ||||
| return toUpperCase (character) != character; | return toUpperCase (character) != character; | ||||
| #endif | #endif | ||||
| @@ -72,7 +72,7 @@ bool CharacterFunctions::isWhitespace (const char character) noexcept | |||||
| bool CharacterFunctions::isWhitespace (const juce_wchar 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 | 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 | 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 | 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 | 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 | 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 | 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 | int CharacterFunctions::getHexDigitValue (const juce_wchar digit) noexcept | ||||
| @@ -40,25 +40,22 @@ Identifier& Identifier::operator= (const Identifier other) noexcept | |||||
| Identifier::Identifier (const String& nm) | Identifier::Identifier (const String& nm) | ||||
| : name (StringPool::getGlobalPool().getPooledString (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) | Identifier::Identifier (const char* nm) | ||||
| : name (StringPool::getGlobalPool().getPooledString (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) | Identifier::Identifier (String::CharPointerType start, String::CharPointerType end) | ||||
| : name (StringPool::getGlobalPool().getPooledString (start, 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; | Identifier Identifier::null; | ||||
| @@ -1617,10 +1617,10 @@ String String::upToLastOccurrenceOf (StringRef sub, | |||||
| bool String::isQuotedString() const | 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 | String String::unquoted() const | ||||
| @@ -2522,14 +2522,13 @@ public: | |||||
| beginTest ("var"); | beginTest ("var"); | ||||
| var v1 = 0; | var v1 = 0; | ||||
| var v2 = 0.1; | |||||
| var v3 = "0.1"; | |||||
| var v2 = 0.16; | |||||
| var v3 = "0.16"; | |||||
| var v4 = (int64) 0; | var v4 = (int64) 0; | ||||
| var v5 = 0.0; | var v5 = 0.0; | ||||
| expect (! v2.equals (v1)); | expect (! v2.equals (v1)); | ||||
| expect (! v1.equals (v2)); | expect (! v1.equals (v2)); | ||||
| expect (v2.equals (v3)); | expect (v2.equals (v3)); | ||||
| expect (v3.equals (v2)); | |||||
| expect (! v3.equals (v1)); | expect (! v3.equals (v1)); | ||||
| expect (! v1.equals (v3)); | expect (! v1.equals (v3)); | ||||
| expect (v1.equals (v4)); | expect (v1.equals (v4)); | ||||
| @@ -299,13 +299,13 @@ public: | |||||
| Note that there's also an isNotEmpty() method to help write readable code. | Note that there's also an isNotEmpty() method to help write readable code. | ||||
| @see containsNonWhitespaceChars() | @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. | /** Returns true if the string contains at least one character. | ||||
| Note that there's also an isEmpty() method to help write readable code. | Note that there's also an isEmpty() method to help write readable code. | ||||
| @see containsNonWhitespaceChars() | @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. */ | /** Resets this string to be empty. */ | ||||
| void clear() noexcept; | void clear() noexcept; | ||||
| @@ -235,7 +235,7 @@ public: | |||||
| /** Finds the thread object that is currently running. | /** Finds the thread object that is currently running. | ||||
| Note that the main UI thread (or other non-Juce threads) don't have a Thread | 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(); | static Thread* JUCE_CALLTYPE getCurrentThread(); | ||||
| @@ -352,7 +352,7 @@ public: | |||||
| /** Returns the value of a named attribute as floating-point. | /** 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). | the String::getDoubleValue() method). | ||||
| @param attributeName the name of the attribute to look up | @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 | 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. | subsequent attempts to call write() will cause an assertion. | ||||
| */ | */ | ||||
| void flush(); | |||||
| void flush() override; | |||||
| int64 getPosition() override; | int64 getPosition() override; | ||||
| bool setPosition (int64) override; | bool setPosition (int64) override; | ||||
| @@ -90,7 +90,7 @@ namespace zlibNamespace | |||||
| class GZIPDecompressorInputStream::GZIPDecompressHelper | class GZIPDecompressorInputStream::GZIPDecompressHelper | ||||
| { | { | ||||
| public: | public: | ||||
| GZIPDecompressHelper (const bool dontWrap) | |||||
| GZIPDecompressHelper (Format f) | |||||
| : finished (true), | : finished (true), | ||||
| needsDictionary (false), | needsDictionary (false), | ||||
| error (true), | error (true), | ||||
| @@ -100,7 +100,7 @@ public: | |||||
| { | { | ||||
| using namespace zlibNamespace; | using namespace zlibNamespace; | ||||
| zerostruct (stream); | zerostruct (stream); | ||||
| streamIsValid = (inflateInit2 (&stream, dontWrap ? -MAX_WBITS : MAX_WBITS) == Z_OK); | |||||
| streamIsValid = (inflateInit2 (&stream, getBitsForFormat (f)) == Z_OK); | |||||
| finished = error = ! streamIsValid; | finished = error = ! streamIsValid; | ||||
| } | } | ||||
| @@ -157,6 +157,19 @@ public: | |||||
| return 0; | 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; | bool finished, needsDictionary, error, streamIsValid; | ||||
| enum { gzipDecompBufferSize = 32768 }; | 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), | : sourceStream (source, deleteSourceWhenDestroyed), | ||||
| uncompressedStreamLength (uncompressedStreamLength_), | |||||
| noWrap (noWrap_), | |||||
| uncompressedStreamLength (uncompressedLength), | |||||
| format (f), | |||||
| isEof (false), | isEof (false), | ||||
| activeBufferSize (0), | activeBufferSize (0), | ||||
| originalSourcePos (source->getPosition()), | originalSourcePos (source->getPosition()), | ||||
| currentPos (0), | currentPos (0), | ||||
| buffer ((size_t) GZIPDecompressHelper::gzipDecompBufferSize), | buffer ((size_t) GZIPDecompressHelper::gzipDecompBufferSize), | ||||
| helper (new GZIPDecompressHelper (noWrap_)) | |||||
| helper (new GZIPDecompressHelper (f)) | |||||
| { | { | ||||
| } | } | ||||
| GZIPDecompressorInputStream::GZIPDecompressorInputStream (InputStream& source) | GZIPDecompressorInputStream::GZIPDecompressorInputStream (InputStream& source) | ||||
| : sourceStream (&source, false), | : sourceStream (&source, false), | ||||
| uncompressedStreamLength (-1), | uncompressedStreamLength (-1), | ||||
| noWrap (false), | |||||
| format (zlibFormat), | |||||
| isEof (false), | isEof (false), | ||||
| activeBufferSize (0), | activeBufferSize (0), | ||||
| originalSourcePos (source.getPosition()), | originalSourcePos (source.getPosition()), | ||||
| currentPos (0), | currentPos (0), | ||||
| buffer ((size_t) GZIPDecompressHelper::gzipDecompBufferSize), | buffer ((size_t) GZIPDecompressHelper::gzipDecompBufferSize), | ||||
| helper (new GZIPDecompressHelper (false)) | |||||
| helper (new GZIPDecompressHelper (zlibFormat)) | |||||
| { | { | ||||
| } | } | ||||
| @@ -278,7 +289,7 @@ bool GZIPDecompressorInputStream::setPosition (int64 newPos) | |||||
| isEof = false; | isEof = false; | ||||
| activeBufferSize = 0; | activeBufferSize = 0; | ||||
| currentPos = 0; | currentPos = 0; | ||||
| helper = new GZIPDecompressHelper (noWrap); | |||||
| helper = new GZIPDecompressHelper (format); | |||||
| sourceStream->setPosition (originalSourcePos); | sourceStream->setPosition (originalSourcePos); | ||||
| } | } | ||||
| @@ -43,21 +43,28 @@ | |||||
| class JUCE_API GZIPDecompressorInputStream : public InputStream | class JUCE_API GZIPDecompressorInputStream : public InputStream | ||||
| { | { | ||||
| public: | public: | ||||
| enum Format | |||||
| { | |||||
| zlibFormat = 0, | |||||
| deflateFormat, | |||||
| gzipFormat | |||||
| }; | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Creates a decompressor stream. | /** Creates a decompressor stream. | ||||
| @param sourceStream the stream to read from | @param sourceStream the stream to read from | ||||
| @param deleteSourceWhenDestroyed whether or not to delete the source stream | @param deleteSourceWhenDestroyed whether or not to delete the source stream | ||||
| when this object is destroyed | 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 | @param uncompressedStreamLength if the creator knows the length that the | ||||
| uncompressed stream will be, then it can supply this | uncompressed stream will be, then it can supply this | ||||
| value, which will be returned by getTotalLength() | value, which will be returned by getTotalLength() | ||||
| */ | */ | ||||
| GZIPDecompressorInputStream (InputStream* sourceStream, | GZIPDecompressorInputStream (InputStream* sourceStream, | ||||
| bool deleteSourceWhenDestroyed, | bool deleteSourceWhenDestroyed, | ||||
| bool noWrap = false, | |||||
| Format sourceFormat = zlibFormat, | |||||
| int64 uncompressedStreamLength = -1); | int64 uncompressedStreamLength = -1); | ||||
| /** Creates a decompressor stream. | /** Creates a decompressor stream. | ||||
| @@ -81,7 +88,7 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| OptionalScopedPointer<InputStream> sourceStream; | OptionalScopedPointer<InputStream> sourceStream; | ||||
| const int64 uncompressedStreamLength; | const int64 uncompressedStreamLength; | ||||
| const bool noWrap; | |||||
| const Format format; | |||||
| bool isEof; | bool isEof; | ||||
| int activeBufferSize; | int activeBufferSize; | ||||
| int64 originalSourcePos, currentPos; | int64 originalSourcePos, currentPos; | ||||
| @@ -91,6 +98,11 @@ private: | |||||
| friend struct ContainerDeletePolicy<GZIPDecompressHelper>; | friend struct ContainerDeletePolicy<GZIPDecompressHelper>; | ||||
| ScopedPointer<GZIPDecompressHelper> helper; | 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) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GZIPDecompressorInputStream) | ||||
| }; | }; | ||||
| @@ -298,7 +298,9 @@ InputStream* ZipFile::createStreamForEntry (const int index) | |||||
| if (zei->compressed) | 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..) | // (much faster to unzip in big blocks using a buffer..) | ||||
| stream = new BufferedInputStream (stream, 32768, true); | 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 */ | ushf *bl_count) /* number of codes at each bit length */ | ||||
| { | { | ||||
| ush next_code[MAX_BITS+1]; /* next code value for 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 bits; /* bit index */ | ||||
| int n; /* code index */ | int n; /* code index */ | ||||
| @@ -579,12 +579,12 @@ local void gen_codes (ct_data *tree, /* the tree to decorate */ | |||||
| * without bit reversal. | * without bit reversal. | ||||
| */ | */ | ||||
| for (bits = 1; bits <= MAX_BITS; bits++) { | 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 | /* Check that the bit counts in bl_count are consistent. The last code | ||||
| * must be all ones. | * 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"); | "inconsistent bit counts"); | ||||
| Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); | 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 */ | unsigned dist; /* distance of matched string */ | ||||
| int lc; /* match length or unmatched char (if dist == 0) */ | int lc; /* match length or unmatched char (if dist == 0) */ | ||||
| unsigned lx = 0; /* running index in l_buf */ | 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 */ | int extra; /* number of extra bits to send */ | ||||
| if (s->last_lit != 0) do { | if (s->last_lit != 0) do { | ||||
| @@ -1066,21 +1066,21 @@ local void compress_block (deflate_state *s, | |||||
| Tracecv(isgraph(lc), (stderr," '%c' ", lc)); | Tracecv(isgraph(lc), (stderr," '%c' ", lc)); | ||||
| } else { | } else { | ||||
| /* Here, lc is the match length - MIN_MATCH */ | /* 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) { | if (extra != 0) { | ||||
| lc -= base_length[code]; | |||||
| lc -= base_length[code_]; | |||||
| send_bits(s, lc, extra); /* send the extra length bits */ | send_bits(s, lc, extra); /* send the extra length bits */ | ||||
| } | } | ||||
| dist--; /* dist is now the match distance - 1 */ | 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) { | if (extra != 0) { | ||||
| dist -= base_dist[code]; | |||||
| dist -= base_dist[code_]; | |||||
| send_bits(s, dist, extra); /* send the extra distance bits */ | send_bits(s, dist, extra); /* send the extra distance bits */ | ||||
| } | } | ||||
| } /* literal or match pair ? */ | } /* literal or match pair ? */ | ||||
| @@ -1120,12 +1120,12 @@ local void set_data_type (deflate_state *s) | |||||
| * method would use a table) | * method would use a table) | ||||
| * IN assertion: 1 <= len <= 15 | * 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; | register unsigned res = 0; | ||||
| do { | do { | ||||
| res |= code & 1; | |||||
| code >>= 1, res <<= 1; | |||||
| res |= code_ & 1; | |||||
| code_ >>= 1, res <<= 1; | |||||
| } while (--len > 0); | } while (--len > 0); | ||||
| return res >> 1; | return res >> 1; | ||||
| } | } | ||||
| @@ -223,7 +223,7 @@ public: | |||||
| protected: | protected: | ||||
| /** @internal */ | /** @internal */ | ||||
| virtual void propertyChanged(); | |||||
| void propertyChanged() override; | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -203,6 +203,14 @@ void UndoManager::setCurrentTransactionName (const String& newName) noexcept | |||||
| action->name = newName; | 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::getCurrentSet() const noexcept { return transactions [nextIndex - 1]; } | ||||
| UndoManager::ActionSet* UndoManager::getNextSet() const noexcept { return transactions [nextIndex]; } | UndoManager::ActionSet* UndoManager::getNextSet() const noexcept { return transactions [nextIndex]; } | ||||
| @@ -144,6 +144,11 @@ public: | |||||
| */ | */ | ||||
| void setCurrentTransactionName (const String& newName) noexcept; | 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. | /** Returns true if there's at least one action in the list to undo. | ||||
| @see getUndoDescription, undo, canRedo | @see getUndoDescription, undo, canRedo | ||||
| @@ -72,12 +72,12 @@ public: | |||||
| { | { | ||||
| } | } | ||||
| var getValue() const | |||||
| var getValue() const override | |||||
| { | { | ||||
| return value; | return value; | ||||
| } | } | ||||
| void setValue (const var& newValue) | |||||
| void setValue (const var& newValue) override | |||||
| { | { | ||||
| if (! newValue.equalsWithSameType (value)) | if (! newValue.equalsWithSameType (value)) | ||||
| { | { | ||||
| @@ -814,8 +814,8 @@ public: | |||||
| tree.removeListener (this); | 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: | private: | ||||
| ValueTree tree; | ValueTree tree; | ||||
| @@ -1003,9 +1003,16 @@ ValueTree ValueTree::readFromStream (InputStream& input) | |||||
| for (int i = 0; i < numProps; ++i) | for (int i = 0; i < numProps; ++i) | ||||
| { | { | ||||
| const String name (input.readString()); | 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(); | const int numChildren = input.readCompressedInt(); | ||||
| @@ -1015,6 +1022,9 @@ ValueTree ValueTree::readFromStream (InputStream& input) | |||||
| { | { | ||||
| ValueTree child (readFromStream (input)); | ValueTree child (readFromStream (input)); | ||||
| if (! child.isValid()) | |||||
| return v; | |||||
| v.object->children.add (child.object); | v.object->children.add (child.object); | ||||
| child.object->parent = v.object; | child.object->parent = v.object; | ||||
| } | } | ||||
| @@ -232,7 +232,7 @@ int JUCEApplicationBase::main() | |||||
| jassert (app != nullptr); | jassert (app != nullptr); | ||||
| if (! app->initialiseApp()) | if (! app->initialiseApp()) | ||||
| return app->getApplicationReturnValue(); | |||||
| return app->shutdownApp(); | |||||
| JUCE_TRY | JUCE_TRY | ||||
| { | { | ||||
| @@ -259,6 +259,7 @@ public: | |||||
| static CreateInstanceFunction createInstance; | static CreateInstanceFunction createInstance; | ||||
| virtual bool initialiseApp(); | virtual bool initialiseApp(); | ||||
| int shutdownApp(); | |||||
| static void JUCE_CALLTYPE sendUnhandledException (const std::exception*, const char* sourceFile, int lineNumber); | static void JUCE_CALLTYPE sendUnhandledException (const std::exception*, const char* sourceFile, int lineNumber); | ||||
| bool sendCommandLineToPreexistingInstance(); | bool sendCommandLineToPreexistingInstance(); | ||||
| #endif | #endif | ||||
| @@ -274,8 +275,6 @@ private: | |||||
| friend struct ContainerDeletePolicy<MultipleInstanceHandler>; | friend struct ContainerDeletePolicy<MultipleInstanceHandler>; | ||||
| ScopedPointer<MultipleInstanceHandler> multipleInstanceHandler; | ScopedPointer<MultipleInstanceHandler> multipleInstanceHandler; | ||||
| int shutdownApp(); | |||||
| JUCE_DECLARE_NON_COPYABLE (JUCEApplicationBase) | JUCE_DECLARE_NON_COPYABLE (JUCEApplicationBase) | ||||
| }; | }; | ||||
| @@ -25,7 +25,15 @@ | |||||
| void MessageManager::runDispatchLoop() | void MessageManager::runDispatchLoop() | ||||
| { | { | ||||
| jassert (isThisTheMessageThread()); // must only be called by the message thread | 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() | void MessageManager::stopDispatchLoop() | ||||
| @@ -34,6 +42,7 @@ void MessageManager::stopDispatchLoop() | |||||
| exit (0); // iOS apps get no mercy.. | exit (0); // iOS apps get no mercy.. | ||||
| } | } | ||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) | bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) | ||||
| { | { | ||||
| JUCE_AUTORELEASEPOOL | JUCE_AUTORELEASEPOOL | ||||
| @@ -59,6 +68,7 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) | |||||
| return ! quitMessagePosted; | return ! quitMessagePosted; | ||||
| } | } | ||||
| } | } | ||||
| #endif | |||||
| //============================================================================== | //============================================================================== | ||||
| static ScopedPointer<MessageQueue> messageQueue; | static ScopedPointer<MessageQueue> messageQueue; | ||||
| @@ -74,7 +74,7 @@ public: | |||||
| ScopedUnlock ul (lock); | ScopedUnlock ul (lock); | ||||
| const unsigned char x = 0xff; | const unsigned char x = 0xff; | ||||
| size_t bytesWritten = write (fd[0], &x, 1); | |||||
| ssize_t bytesWritten = write (fd[0], &x, 1); | |||||
| (void) bytesWritten; | (void) bytesWritten; | ||||
| } | } | ||||
| } | } | ||||
| @@ -186,7 +186,7 @@ private: | |||||
| const ScopedUnlock ul (lock); | const ScopedUnlock ul (lock); | ||||
| unsigned char x; | unsigned char x; | ||||
| size_t numBytes = read (fd[1], &x, 1); | |||||
| ssize_t numBytes = read (fd[1], &x, 1); | |||||
| (void) numBytes; | (void) numBytes; | ||||
| } | } | ||||
| @@ -235,6 +235,8 @@ namespace LinuxErrorHandling | |||||
| int errorHandler (Display* display, XErrorEvent* event) | int errorHandler (Display* display, XErrorEvent* event) | ||||
| { | { | ||||
| (void) display; (void) event; | |||||
| #if JUCE_DEBUG_XERRORS | #if JUCE_DEBUG_XERRORS | ||||
| char errorStr[64] = { 0 }; | char errorStr[64] = { 0 }; | ||||
| char requestStr[64] = { 0 }; | char requestStr[64] = { 0 }; | ||||
| @@ -261,11 +261,13 @@ static void shutdownNSApp() | |||||
| void MessageManager::stopDispatchLoop() | void MessageManager::stopDispatchLoop() | ||||
| { | { | ||||
| #if JUCE_PROJUCER_LIVE_BUILD | |||||
| quitMessagePosted = true; | quitMessagePosted = true; | ||||
| #else | |||||
| #if ! JUCE_PROJUCER_LIVE_BUILD | |||||
| if (isThisTheMessageThread()) | if (isThisTheMessageThread()) | ||||
| { | { | ||||
| quitMessagePosted = true; | |||||
| shutdownNSApp(); | shutdownNSApp(); | ||||
| } | } | ||||
| else | else | ||||
| @@ -273,7 +275,7 @@ void MessageManager::stopDispatchLoop() | |||||
| struct QuitCallback : public CallbackMessage | struct QuitCallback : public CallbackMessage | ||||
| { | { | ||||
| QuitCallback() {} | QuitCallback() {} | ||||
| void messageCallback() override { shutdownNSApp(); } | |||||
| void messageCallback() override { MessageManager::getInstance()->stopDispatchLoop(); } | |||||
| }; | }; | ||||
| (new QuitCallback())->post(); | (new QuitCallback())->post(); | ||||
| @@ -112,10 +112,10 @@ public: | |||||
| { | { | ||||
| const LockType::ScopedLockType sl (lock); | const LockType::ScopedLockType sl (lock); | ||||
| while (firstTimer != nullptr && firstTimer->countdownMs <= 0) | |||||
| while (firstTimer != nullptr && firstTimer->timerCountdownMs <= 0) | |||||
| { | { | ||||
| Timer* const t = firstTimer; | Timer* const t = firstTimer; | ||||
| t->countdownMs = t->periodMs; | |||||
| t->timerCountdownMs = t->timerPeriodMs; | |||||
| removeTimer (t); | removeTimer (t); | ||||
| addTimer (t); | addTimer (t); | ||||
| @@ -169,11 +169,11 @@ public: | |||||
| { | { | ||||
| if (instance != nullptr) | 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->removeTimer (tim); | ||||
| instance->addTimer (tim); | instance->addTimer (tim); | ||||
| @@ -210,28 +210,28 @@ private: | |||||
| Timer* i = firstTimer; | 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; | firstTimer = t; | ||||
| } | } | ||||
| else | 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); | 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(); | notify(); | ||||
| } | } | ||||
| @@ -244,32 +244,32 @@ private: | |||||
| jassert (timerExists (t)); | jassert (timerExists (t)); | ||||
| #endif | #endif | ||||
| if (t->previous != nullptr) | |||||
| if (t->previousTimer != nullptr) | |||||
| { | { | ||||
| jassert (firstTimer != t); | jassert (firstTimer != t); | ||||
| t->previous->next = t->next; | |||||
| t->previousTimer->nextTimer = t->nextTimer; | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| jassert (firstTimer == t); | 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 | int getTimeUntilFirstTimer (const int numMillisecsElapsed) const | ||||
| { | { | ||||
| const LockType::ScopedLockType sl (lock); | 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 | void handleAsyncUpdate() override | ||||
| @@ -280,7 +280,7 @@ private: | |||||
| #if JUCE_DEBUG | #if JUCE_DEBUG | ||||
| bool timerExists (Timer* const t) const noexcept | 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) | if (tt == t) | ||||
| return true; | return true; | ||||
| @@ -296,18 +296,18 @@ Timer::TimerThread::LockType Timer::TimerThread::lock; | |||||
| //============================================================================== | //============================================================================== | ||||
| Timer::Timer() noexcept | Timer::Timer() noexcept | ||||
| : countdownMs (0), | |||||
| periodMs (0), | |||||
| previous (nullptr), | |||||
| next (nullptr) | |||||
| : timerCountdownMs (0), | |||||
| timerPeriodMs (0), | |||||
| previousTimer (nullptr), | |||||
| nextTimer (nullptr) | |||||
| { | { | ||||
| } | } | ||||
| Timer::Timer (const Timer&) noexcept | 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); | 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); | TimerThread::add (this); | ||||
| } | } | ||||
| else | else | ||||
| @@ -344,10 +344,10 @@ void Timer::stopTimer() noexcept | |||||
| { | { | ||||
| const TimerThread::LockType::ScopedLockType sl (TimerThread::lock); | const TimerThread::LockType::ScopedLockType sl (TimerThread::lock); | ||||
| if (periodMs > 0) | |||||
| if (timerPeriodMs > 0) | |||||
| { | { | ||||
| TimerThread::remove (this); | TimerThread::remove (this); | ||||
| periodMs = 0; | |||||
| timerPeriodMs = 0; | |||||
| } | } | ||||
| } | } | ||||
| @@ -54,7 +54,6 @@ class JUCE_API Timer | |||||
| protected: | protected: | ||||
| //============================================================================== | //============================================================================== | ||||
| /** Creates a Timer. | /** Creates a Timer. | ||||
| When created, the timer is stopped, so use startTimer() to get it going. | When created, the timer is stopped, so use startTimer() to get it going. | ||||
| */ | */ | ||||
| Timer() noexcept; | Timer() noexcept; | ||||
| @@ -64,7 +63,7 @@ protected: | |||||
| Note that this timer won't be started, even if the one you're copying | Note that this timer won't be started, even if the one you're copying | ||||
| is running. | is running. | ||||
| */ | */ | ||||
| Timer (const Timer& other) noexcept; | |||||
| Timer (const Timer&) noexcept; | |||||
| public: | public: | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -86,8 +85,8 @@ public: | |||||
| time between calling this method and the next timer callback | time between calling this method and the next timer callback | ||||
| will not be less than the interval length passed in. | 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; | void startTimer (int intervalInMilliseconds) noexcept; | ||||
| @@ -108,12 +107,12 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Returns true if the timer is currently running. */ | /** 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. | ||||
| @returns the timer's interval in milliseconds if it's running, or 0 if it's not. | @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: | private: | ||||
| class TimerThread; | class TimerThread; | ||||
| friend 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 | #endif // JUCE_TIMER_H_INCLUDED | ||||
| @@ -136,7 +136,7 @@ namespace ColourHelpers | |||||
| //============================================================================== | //============================================================================== | ||||
| Colour::Colour() noexcept | Colour::Colour() noexcept | ||||
| : argb (0) | |||||
| : argb (0, 0, 0, 0) | |||||
| { | { | ||||
| } | } | ||||
| @@ -151,11 +151,12 @@ Colour& Colour::operator= (const Colour& other) noexcept | |||||
| return *this; | 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 | Colour::~Colour() noexcept | ||||
| { | { | ||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| const PixelARGB Colour::getPixelARGB() const noexcept | const PixelARGB Colour::getPixelARGB() const noexcept | ||||
| { | { | ||||
| @@ -220,7 +237,7 @@ const PixelARGB Colour::getPixelARGB() const noexcept | |||||
| uint32 Colour::getARGB() 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); | PixelARGB newCol (argb); | ||||
| newCol.setAlpha (newAlpha); | newCol.setAlpha (newAlpha); | ||||
| return Colour (newCol.getARGB()); | |||||
| return Colour (newCol); | |||||
| } | } | ||||
| Colour Colour::withAlpha (const float newAlpha) const noexcept | Colour Colour::withAlpha (const float newAlpha) const noexcept | ||||
| @@ -247,7 +264,7 @@ Colour Colour::withAlpha (const float newAlpha) const noexcept | |||||
| PixelARGB newCol (argb); | PixelARGB newCol (argb); | ||||
| newCol.setAlpha (ColourHelpers::floatToUInt8 (newAlpha)); | newCol.setAlpha (ColourHelpers::floatToUInt8 (newAlpha)); | ||||
| return Colour (newCol.getARGB()); | |||||
| return Colour (newCol); | |||||
| } | } | ||||
| Colour Colour::withMultipliedAlpha (const float alphaMultiplier) const noexcept | Colour Colour::withMultipliedAlpha (const float alphaMultiplier) const noexcept | ||||
| @@ -256,7 +273,7 @@ Colour Colour::withMultipliedAlpha (const float alphaMultiplier) const noexcept | |||||
| PixelARGB newCol (argb); | PixelARGB newCol (argb); | ||||
| newCol.setAlpha ((uint8) jmin (0xff, roundToInt (alphaMultiplier * newCol.getAlpha()))); | 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.tween (c2, (uint32) roundToInt (proportionOfOther * 255.0f)); | ||||
| c1.unpremultiply(); | c1.unpremultiply(); | ||||
| return Colour (c1.getARGB()); | |||||
| return Colour (c1); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -428,7 +445,7 @@ Colour Colour::contrasting (Colour colour1, | |||||
| //============================================================================== | //============================================================================== | ||||
| String Colour::toString() const | String Colour::toString() const | ||||
| { | { | ||||
| return String::toHexString ((int) argb.getARGB()); | |||||
| return String::toHexString ((int) argb.getInARGBMaskOrder()); | |||||
| } | } | ||||
| Colour Colour::fromString (StringRef encodedColourString) | Colour Colour::fromString (StringRef encodedColourString) | ||||
| @@ -438,7 +455,7 @@ Colour Colour::fromString (StringRef encodedColourString) | |||||
| String Colour::toDisplayString (const bool includeAlphaValue) const | 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) | .paddedLeft ('0', includeAlphaValue ? 8 : 6) | ||||
| .toUpperCase(); | .toUpperCase(); | ||||
| } | } | ||||
| @@ -115,6 +115,19 @@ public: | |||||
| float brightness, | float brightness, | ||||
| float alpha) noexcept; | 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. | /** 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. | 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. | operations with it. | ||||
| This is used internally by the imaging classes. | This is used internally by the imaging classes. | ||||
| @@ -60,13 +60,6 @@ public: | |||||
| PixelARGB() noexcept {} | PixelARGB() noexcept {} | ||||
| ~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 | PixelARGB (const uint8 a, const uint8 r, const uint8 g, const uint8 b) noexcept | ||||
| { | { | ||||
| components.b = b; | components.b = b; | ||||
| @@ -75,30 +68,81 @@ public: | |||||
| components.a = a; | 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 | #if JUCE_GCC && ! JUCE_CLANG | ||||
| // NB these are here as a workaround because GCC refuses to bind to packed values. | // 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 | #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 | #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. | /** Blends another pixel onto this one. | ||||
| This takes into account the opacity of the pixel being overlaid, and blends | This takes into account the opacity of the pixel being overlaid, and blends | ||||
| @@ -107,10 +151,15 @@ public: | |||||
| template <class Pixel> | template <class Pixel> | ||||
| forcedinline void blend (const Pixel& src) noexcept | 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. | /** Blends another pixel onto this one. | ||||
| @@ -129,14 +178,15 @@ public: | |||||
| template <class Pixel> | template <class Pixel> | ||||
| forcedinline void blend (const Pixel& src, uint32 extraAlpha) noexcept | 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); | 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 | /** Blends another pixel with this one, creating a colour that is somewhere | ||||
| @@ -145,29 +195,20 @@ public: | |||||
| template <class Pixel> | template <class Pixel> | ||||
| forcedinline void tween (const Pixel& src, const uint32 amount) noexcept | 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. */ | /** Replaces the colour's alpha value with another one. */ | ||||
| forcedinline void setAlpha (const uint8 newAlpha) noexcept | forcedinline void setAlpha (const uint8 newAlpha) noexcept | ||||
| { | { | ||||
| @@ -177,10 +218,12 @@ public: | |||||
| /** Multiplies the colour's alpha value with another one. */ | /** Multiplies the colour's alpha value with another one. */ | ||||
| forcedinline void multiplyAlpha (int multiplier) noexcept | 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; | ++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 | forcedinline void multiplyAlpha (const float multiplier) noexcept | ||||
| @@ -188,14 +231,8 @@ public: | |||||
| multiplyAlpha ((int) (multiplier * 255.0f)); | 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. */ | /** Premultiplies the pixel's RGB values by its alpha. */ | ||||
| forcedinline void premultiply() noexcept | forcedinline void premultiply() noexcept | ||||
| @@ -234,9 +271,9 @@ public: | |||||
| } | } | ||||
| else | 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. */ | /** 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 | #if JUCE_BIG_ENDIAN | ||||
| enum { indexA = 0, indexR = 1, indexG = 2, indexB = 3 }; | enum { indexA = 0, indexR = 1, indexG = 2, indexB = 3 }; | ||||
| #else | #else | ||||
| enum { indexA = 3, indexR = 2, indexG = 1, indexB = 0 }; | enum { indexA = 3, indexR = 2, indexG = 1, indexB = 0 }; | ||||
| #endif | #endif | ||||
| #endif | |||||
| private: | private: | ||||
| //============================================================================== | |||||
| PixelARGB (const uint32 internalValue) noexcept | |||||
| : internal (internalValue) | |||||
| { | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| struct Components | 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 | #if JUCE_BIG_ENDIAN | ||||
| uint8 a, r, g, b; | uint8 a, r, g, b; | ||||
| #else | #else | ||||
| uint8 b, g, r, a; | uint8 b, g, r, a; | ||||
| #endif | #endif | ||||
| #endif | |||||
| } JUCE_PACKED; | } JUCE_PACKED; | ||||
| union | union | ||||
| { | { | ||||
| uint32 argb; | |||||
| uint32 internal; | |||||
| Components components; | Components components; | ||||
| #if JUCE_GCC | #if JUCE_GCC | ||||
| uint8 comps[4]; | |||||
| uint8 comps[4]; // helper struct needed because gcc does not allow references to packed union members | |||||
| #endif | #endif | ||||
| }; | }; | ||||
| } | } | ||||
| @@ -316,23 +365,64 @@ public: | |||||
| PixelRGB() noexcept {} | PixelRGB() noexcept {} | ||||
| ~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 getAlpha() const noexcept { return 0xff; } | ||||
| forcedinline uint8 getRed() const noexcept { return r; } | forcedinline uint8 getRed() const noexcept { return r; } | ||||
| forcedinline uint8 getGreen() const noexcept { return g; } | forcedinline uint8 getGreen() const noexcept { return g; } | ||||
| @@ -342,6 +432,30 @@ public: | |||||
| forcedinline uint8& getGreen() noexcept { return g; } | forcedinline uint8& getGreen() noexcept { return g; } | ||||
| forcedinline uint8& getBlue() noexcept { return b; } | 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. | /** Blends another pixel onto this one. | ||||
| This takes into account the opacity of the pixel being overlaid, and blends | This takes into account the opacity of the pixel being overlaid, and blends | ||||
| @@ -350,14 +464,22 @@ public: | |||||
| template <class Pixel> | template <class Pixel> | ||||
| forcedinline void blend (const Pixel& src) noexcept | 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); | r = (uint8) (rb >> 16); | ||||
| g = (uint8) clampPixelComponents (ag); | |||||
| b = (uint8) rb; | |||||
| b = (uint8) (rb & 0xff); | |||||
| #endif | |||||
| } | } | ||||
| forcedinline void blend (const PixelRGB src) noexcept | forcedinline void blend (const PixelRGB src) noexcept | ||||
| @@ -373,16 +495,23 @@ public: | |||||
| template <class Pixel> | template <class Pixel> | ||||
| forcedinline void blend (const Pixel& src, uint32 extraAlpha) noexcept | 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); | 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); | r = (uint8) (rb >> 16); | ||||
| b = (uint8) (rb & 0xff); | |||||
| #endif | |||||
| } | } | ||||
| /** Blends another pixel with this one, creating a colour that is somewhere | /** Blends another pixel with this one, creating a colour that is somewhere | ||||
| @@ -391,31 +520,24 @@ public: | |||||
| template <class Pixel> | template <class Pixel> | ||||
| forcedinline void tween (const Pixel& src, const uint32 amount) noexcept | 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. */ | /** This method is included for compatibility with the PixelARGB class. */ | ||||
| forcedinline void setAlpha (const uint8) noexcept {} | forcedinline void setAlpha (const uint8) noexcept {} | ||||
| @@ -425,14 +547,6 @@ public: | |||||
| /** Multiplies the colour's alpha value with another one. */ | /** Multiplies the colour's alpha value with another one. */ | ||||
| forcedinline void multiplyAlpha (float) noexcept {} | 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. */ | /** Premultiplies the pixel's RGB values by its alpha. */ | ||||
| forcedinline void premultiply() noexcept {} | forcedinline void premultiply() noexcept {} | ||||
| @@ -454,6 +568,20 @@ public: | |||||
| private: | 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 | #if JUCE_MAC | ||||
| uint8 r, g, b; | uint8 r, g, b; | ||||
| #else | #else | ||||
| @@ -486,21 +614,36 @@ public: | |||||
| PixelAlpha() noexcept {} | PixelAlpha() noexcept {} | ||||
| ~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() const noexcept { return a; } | ||||
| forcedinline uint8& getAlpha() noexcept { return a; } | forcedinline uint8& getAlpha() noexcept { return a; } | ||||
| @@ -508,6 +651,24 @@ public: | |||||
| forcedinline uint8 getGreen() const noexcept { return 0; } | forcedinline uint8 getGreen() const noexcept { return 0; } | ||||
| forcedinline uint8 getBlue() 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. | /** Blends another pixel onto this one. | ||||
| This takes into account the opacity of the pixel being overlaid, and blends | This takes into account the opacity of the pixel being overlaid, and blends | ||||
| @@ -542,16 +703,7 @@ public: | |||||
| a += ((src.getAlpha() - a) * amount) >> 8; | 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. */ | /** Replaces the colour's alpha value with another one. */ | ||||
| forcedinline void setAlpha (const uint8 newAlpha) noexcept | forcedinline void setAlpha (const uint8 newAlpha) noexcept | ||||
| { | { | ||||
| @@ -570,12 +722,6 @@ public: | |||||
| a = (uint8) (a * multiplier); | 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. */ | /** Premultiplies the pixel's RGB values by its alpha. */ | ||||
| forcedinline void premultiply() noexcept {} | forcedinline void premultiply() noexcept {} | ||||
| @@ -589,6 +735,12 @@ public: | |||||
| enum { indexA = 0 }; | enum { indexA = 0 }; | ||||
| private: | private: | ||||
| //============================================================================== | |||||
| PixelAlpha (const uint32 internal) noexcept | |||||
| { | |||||
| a = (uint8) (internal >> 24); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| uint8 a; | uint8 a; | ||||
| } | } | ||||
| @@ -203,7 +203,7 @@ public: | |||||
| The minimumHorizontalScale parameter specifies how much the text can be squashed horizontally | 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 | 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 | @see GlyphArrangement::addFittedText | ||||
| */ | */ | ||||
| @@ -211,7 +211,7 @@ public: | |||||
| int x, int y, int width, int height, | int x, int y, int width, int height, | ||||
| Justification justificationFlags, | Justification justificationFlags, | ||||
| int maximumNumberOfLines, | int maximumNumberOfLines, | ||||
| float minimumHorizontalScale = 0.7f) const; | |||||
| float minimumHorizontalScale = 0.0f) const; | |||||
| /** Tries to draw a text string inside a given space. | /** 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 | 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 | 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 | @see GlyphArrangement::addFittedText | ||||
| */ | */ | ||||
| @@ -236,7 +236,7 @@ public: | |||||
| const Rectangle<int>& area, | const Rectangle<int>& area, | ||||
| Justification justificationFlags, | Justification justificationFlags, | ||||
| int maximumNumberOfLines, | 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. | /** Fills the context's entire clip region with the current colour or brush. | ||||
| @@ -374,21 +374,29 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Draws a line between two points. | /** Draws a line between two points. | ||||
| The line is 1 pixel wide and drawn with the current colour or brush. | 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; | void drawLine (float startX, float startY, float endX, float endY) const; | ||||
| /** Draws a line between two points with a given thickness. | /** 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 | @see Path::addLineSegment | ||||
| */ | */ | ||||
| void drawLine (float startX, float startY, float endX, float endY, float lineThickness) const; | void drawLine (float startX, float startY, float endX, float endY, float lineThickness) const; | ||||
| /** Draws a line between two points. | /** Draws a line between two points. | ||||
| The line is 1 pixel wide and drawn with the current colour or brush. | 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; | void drawLine (const Line<float>& line) const; | ||||
| /** Draws a line between two points with a given thickness. | /** Draws a line between two points with a given thickness. | ||||
| @see Path::addLineSegment | @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; | void drawLine (const Line<float>& line, float lineThickness) const; | ||||
| @@ -433,11 +433,11 @@ void LowLevelGraphicsPostScriptRenderer::writeImage (const Image& im, | |||||
| { | { | ||||
| PixelARGB p (*(const PixelARGB*) pixelData); | PixelARGB p (*(const PixelARGB*) pixelData); | ||||
| p.unpremultiply(); | p.unpremultiply(); | ||||
| pixel = Colours::white.overlaidWith (Colour (p.getARGB())); | |||||
| pixel = Colours::white.overlaidWith (Colour (p)); | |||||
| } | } | ||||
| else if (im.isRGB()) | else if (im.isRGB()) | ||||
| { | { | ||||
| pixel = Colour (((const PixelRGB*) pixelData)->getARGB()); | |||||
| pixel = Colour (*((const PixelRGB*) pixelData)); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -30,6 +30,7 @@ namespace FontValues | |||||
| } | } | ||||
| const float defaultFontHeight = 14.0f; | const float defaultFontHeight = 14.0f; | ||||
| float minimumHorizontalScale = 0.7f; | |||||
| String fallbackFont; | String fallbackFont; | ||||
| String fallbackFontStyle; | String fallbackFontStyle; | ||||
| } | } | ||||
| @@ -37,6 +38,9 @@ namespace FontValues | |||||
| typedef Typeface::Ptr (*GetTypefaceForFont) (const Font&); | typedef Typeface::Ptr (*GetTypefaceForFont) (const Font&); | ||||
| GetTypefaceForFont juce_getTypefaceForFont = nullptr; | GetTypefaceForFont juce_getTypefaceForFont = nullptr; | ||||
| float Font::getDefaultMinimumHorizontalScaleFactor() noexcept { return FontValues::minimumHorizontalScale; } | |||||
| void Font::setDefaultMinimumHorizontalScaleFactor (float newValue) noexcept { FontValues::minimumHorizontalScale = newValue; } | |||||
| //============================================================================== | //============================================================================== | ||||
| class TypefaceCache : private DeletedAtShutdown | class TypefaceCache : private DeletedAtShutdown | ||||
| { | { | ||||
| @@ -319,6 +319,18 @@ public: | |||||
| */ | */ | ||||
| void setHorizontalScale (float scaleFactor); | 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. | /** Returns the font's kerning. | ||||
| This is the extra space added between adjacent characters, as a proportion | 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, | const float width, const float height, | ||||
| Justification layout, | Justification layout, | ||||
| int maximumLines, | 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 | // doesn't make much sense if this is outside a sensible range of 0.5 to 1.0 | ||||
| jassert (minimumHorizontalScale > 0 && minimumHorizontalScale <= 1.0f); | 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, | A Justification parameter lets you specify how the text is laid out within the rectangle, | ||||
| both horizontally and vertically. | 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 | @see Graphics::drawFittedText | ||||
| */ | */ | ||||
| void addFittedText (const Font& font, | void addFittedText (const Font& font, | ||||
| @@ -215,7 +219,7 @@ public: | |||||
| float x, float y, float width, float height, | float x, float y, float width, float height, | ||||
| Justification layout, | Justification layout, | ||||
| int maximumLinesToUse, | int maximumLinesToUse, | ||||
| float minimumHorizontalScale = 0.7f); | |||||
| float minimumHorizontalScale = 0.0f); | |||||
| /** Appends another glyph arrangement to this one. */ | /** Appends another glyph arrangement to this one. */ | ||||
| void addGlyphArrangement (const GlyphArrangement&); | void addGlyphArrangement (const GlyphArrangement&); | ||||
| @@ -635,4 +635,9 @@ void TextLayout::recalculateSize (const AttributedString& text) | |||||
| width = bounds.getWidth(); | width = bounds.getWidth(); | ||||
| height = bounds.getHeight(); | height = bounds.getHeight(); | ||||
| } | } | ||||
| else | |||||
| { | |||||
| width = 0; | |||||
| height = 0; | |||||
| } | |||||
| } | } | ||||
| @@ -224,8 +224,8 @@ public: | |||||
| if (length <= 0) | if (length <= 0) | ||||
| return start; | 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 | /** 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) | if (prop >= 0 && prop <= 1.0) | ||||
| { | { | ||||
| pointOnLine = start + delta * static_cast <ValueType> (prop); | |||||
| pointOnLine = start + delta * static_cast<ValueType> (prop); | |||||
| return targetPoint.getDistanceFrom (pointOnLine); | return targetPoint.getDistanceFrom (pointOnLine); | ||||
| } | } | ||||
| } | } | ||||
| @@ -301,9 +301,9 @@ public: | |||||
| const double length = delta.x * delta.x + delta.y * delta.y; | const double length = delta.x * delta.x + delta.y * delta.y; | ||||
| return length <= 0 ? 0 | 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. | /** 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; | const ValueType along = (p1.y - p3.y) / d2.y; | ||||
| intersection = p1.withX (p3.x + along * d2.x); | 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) | else if (d2.y == 0 && d1.y != 0) | ||||
| { | { | ||||
| const ValueType along = (p3.y - p1.y) / d1.y; | const ValueType along = (p3.y - p1.y) / d1.y; | ||||
| intersection = p3.withX (p1.x + along * d1.x); | 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) | else if (d1.x == 0 && d2.x != 0) | ||||
| { | { | ||||
| const ValueType along = (p1.x - p3.x) / d2.x; | const ValueType along = (p1.x - p3.x) / d2.x; | ||||
| intersection = p1.withY (p3.y + along * d2.y); | 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) | else if (d2.x == 0 && d1.x != 0) | ||||
| { | { | ||||
| const ValueType along = (p3.x - p1.x) / d1.x; | const ValueType along = (p3.x - p1.x) / d1.x; | ||||
| intersection = p3.withY (p1.y + along * d1.y); | 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; | return false; | ||||
| } | } | ||||
| const ValueType along1 = ((p1.y - p3.y) * d2.x - (p1.x - p3.x) * d2.y) / divisor; | const ValueType along1 = ((p1.y - p3.y) * d2.x - (p1.x - p3.x) * d2.y) / divisor; | ||||
| intersection = p1 + d1 * along1; | intersection = p1 + d1 * along1; | ||||
| if (along1 < 0 || along1 > static_cast <ValueType> (1)) | |||||
| if (along1 < 0 || along1 > static_cast<ValueType> (1)) | |||||
| return false; | return false; | ||||
| const ValueType along2 = ((p1.y - p3.y) * d1.x - (p1.x - p3.x) * d1.y) / divisor; | 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; | const float* const elements = path.data.elements; | ||||
| @@ -499,21 +499,20 @@ public: | |||||
| /** Adds a "pie-chart" shape to the path. | /** 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 | 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 | 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. | 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 | @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 | @see addArc | ||||
| */ | */ | ||||
| void addPieSegment (Rectangle<float> segmentBounds, | void addPieSegment (Rectangle<float> segmentBounds, | ||||
| @@ -718,8 +717,8 @@ public: | |||||
| { | { | ||||
| public: | public: | ||||
| //============================================================================== | //============================================================================== | ||||
| Iterator (const Path& path); | |||||
| ~Iterator(); | |||||
| Iterator (const Path& path) noexcept; | |||||
| ~Iterator() noexcept; | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Moves onto the next element in the path. | /** 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, | 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. | and some of the x and y variables will be filled in with values. | ||||
| */ | */ | ||||
| bool next(); | |||||
| bool next() noexcept; | |||||
| //============================================================================== | //============================================================================== | ||||
| enum PathElementType | enum PathElementType | ||||