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