diff --git a/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h b/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h index b3f49a63d..17cc48c0a 100644 --- a/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h +++ b/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h @@ -487,13 +487,10 @@ public: } /** Scans a block of data, returning the lowest and highest levels as floats */ - void findMinAndMax (size_t numSamples, float& minValue, float& maxValue) const noexcept + Range findMinAndMax (size_t numSamples) const noexcept { if (numSamples == 0) - { - minValue = maxValue = 0; - return; - } + return Range(); Pointer dest (*this); @@ -512,27 +509,32 @@ public: if (v < mn) mn = v; } - minValue = mn; - maxValue = mx; + return Range (mn, mx); } - else + + int32 mn = dest.getAsInt32(); + dest.advance(); + int32 mx = mn; + + while (--numSamples > 0) { - int32 mn = dest.getAsInt32(); + const int v = dest.getAsInt32(); dest.advance(); - int32 mx = mn; - while (--numSamples > 0) - { - const int v = dest.getAsInt32(); - dest.advance(); + if (mx < v) mx = v; + if (v < mn) mn = v; + } - if (mx < v) mx = v; - if (v < mn) mn = v; - } + return Range (mn * (float) (1.0 / (1.0 + Int32::maxValue)), + mx * (float) (1.0 / (1.0 + Int32::maxValue))); + } - minValue = mn * (float) (1.0 / (1.0 + Int32::maxValue)); - maxValue = mx * (float) (1.0 / (1.0 + Int32::maxValue)); - } + /** Scans a block of data, returning the lowest and highest levels as floats */ + void findMinAndMax (size_t numSamples, float& minValue, float& maxValue) const noexcept + { + Range r (findMinAndMax (numSamples)); + minValue = r.getStart(); + maxValue = r.getEnd(); } /** Returns true if the pointer is using a floating-point format. */ diff --git a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp index 2fc6898cb..67a7297a5 100644 --- a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp +++ b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp @@ -134,6 +134,19 @@ namespace FloatVectorHelpers } \ JUCE_FINISH_VEC_OP (normalOp) + #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST(normalOp, vecOp, locals, increment, setupOp) \ + JUCE_BEGIN_VEC_OP \ + setupOp \ + { \ + 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); \ + } \ + JUCE_FINISH_VEC_OP (normalOp) + + //============================================================================== #elif JUCE_USE_ARM_NEON @@ -211,6 +224,13 @@ namespace FloatVectorHelpers 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 \ + JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadU, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ + JUCE_FINISH_VEC_OP (normalOp) + + //============================================================================== #else #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \ @@ -221,6 +241,10 @@ namespace FloatVectorHelpers #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \ for (int i = 0; i < num; ++i) normalOp; + + #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST(normalOp, vecOp, locals, increment, setupOp) \ + for (int i = 0; i < num; ++i) normalOp; + #endif //============================================================================== @@ -240,11 +264,20 @@ namespace FloatVectorHelpers increment; \ } + #define JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD(vecOp, src1Load, src2Load, dstLoad, dstStore, locals, increment) \ + for (int i = 0; i < numLongOps; ++i) \ + { \ + locals (src1Load, src2Load, dstLoad); \ + dstStore (dest, vecOp); \ + increment; \ + } + #define JUCE_LOAD_NONE(srcLoad, dstLoad) - #define JUCE_LOAD_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest); - #define JUCE_LOAD_SRC(srcLoad, dstLoad) const Mode::ParallelType s = srcLoad (src); - #define JUCE_LOAD_SRC1_SRC2(src1Load, src2Load) const Mode::ParallelType s1 = src1Load (src1), s2 = src2Load (src2); - #define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest), s = srcLoad (src); + #define JUCE_LOAD_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest); + #define JUCE_LOAD_SRC(srcLoad, dstLoad) const Mode::ParallelType s = srcLoad (src); + #define JUCE_LOAD_SRC1_SRC2(src1Load, src2Load) const Mode::ParallelType s1 = src1Load (src1), s2 = src2Load (src2); + #define JUCE_LOAD_SRC1_SRC2_DEST(src1Load, src2Load, dstLoad) const Mode::ParallelType d = dstLoad (dest), s1 = src1Load (src1), s2 = src2Load (src2); + #define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest), s = srcLoad (src); #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON template struct ModeType { typedef BasicOps32 Mode; }; @@ -580,6 +613,28 @@ void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (double* dest, const d const Mode::ParallelType mult = Mode::load1 (multiplier);) } +void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (float* dest, const float* src1, const float* src2, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vma ((float*) src1, 1, (float*) src2, 1, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] += src1[i] * src2[i], Mode::add (d, Mode::mul (s1, s2)), + JUCE_LOAD_SRC1_SRC2_DEST, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (double* dest, const double* src1, const double* src2, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmaD ((double*) src1, 1, (double*) src2, 1, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] += src1[i] * src2[i], Mode::add (d, Mode::mul (s1, s2)), + JUCE_LOAD_SRC1_SRC2_DEST, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif +} + void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK @@ -682,6 +737,96 @@ void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, cons #endif } +void JUCE_CALLTYPE FloatVectorOperations::min (float* dest, const float* src, float comp, int num) noexcept +{ + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmin (src[i], comp), Mode::min (s, cmp), + JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType cmp = Mode::load1 (comp);) +} + +void JUCE_CALLTYPE FloatVectorOperations::min (double* dest, const double* src, double comp, int num) noexcept +{ + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmin (src[i], comp), Mode::min (s, cmp), + JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType cmp = Mode::load1 (comp);) +} + +void JUCE_CALLTYPE FloatVectorOperations::min (float* dest, const float* src1, const float* src2, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmin ((float*) src1, 1, (float*) src2, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmin (src1[i], src2[i]), Mode::min (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::min (double* dest, const double* src1, const double* src2, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vminD ((double*) src1, 1, (double*) src2, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmin (src1[i], src2[i]), Mode::min (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::max (float* dest, const float* src, float comp, int num) noexcept +{ + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (src[i], comp), Mode::max (s, cmp), + JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType cmp = Mode::load1 (comp);) +} + +void JUCE_CALLTYPE FloatVectorOperations::max (double* dest, const double* src, double comp, int num) noexcept +{ + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (src[i], comp), Mode::max (s, cmp), + JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType cmp = Mode::load1 (comp);) +} + +void JUCE_CALLTYPE FloatVectorOperations::max (float* dest, const float* src1, const float* src2, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmax ((float*) src1, 1, (float*) src2, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmax (src1[i], src2[i]), Mode::max (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::max (double* dest, const double* src1, const double* src2, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmaxD ((double*) src1, 1, (double*) src2, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmax (src1[i], src2[i]), Mode::max (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::clip (float* dest, const float* src, float low, float high, int num) noexcept +{ + jassert(high >= low); + + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vclip ((float*) src, 1, &low, &high, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (jmin (src[i], high), low), Mode::max (Mode::min (s, hi), lo), + JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType lo = Mode::load1 (low); const Mode::ParallelType hi = Mode::load1 (high);) + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::clip (double* dest, const double* src, double low, double high, int num) noexcept +{ + jassert(high >= low); + + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vclipD ((double*) src, 1, &low, &high, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (jmin (src[i], high), low), Mode::max (Mode::min (s, hi), lo), + JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType lo = Mode::load1 (low); const Mode::ParallelType hi = Mode::load1 (high);) + #endif +} + Range JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const float* src, int num) noexcept { #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON @@ -823,6 +968,11 @@ public: fillRandomly (random, int1, num); doConversionTest (u, data1, data2, int1, num); + + FloatVectorOperations::fill (data1, (ValueType) 2, num); + FloatVectorOperations::fill (data2, (ValueType) 3, num); + FloatVectorOperations::addWithMultiply (data1, data1, data2, num); + u.expect (areAllValuesEqual (data1, num, (ValueType) 8)); } static void doConversionTest (UnitTest& u, float* data1, float* data2, int* const int1, int num) diff --git a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h index 0b3fcb67c..b7dc972da 100644 --- a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h +++ b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h @@ -101,6 +101,12 @@ public: /** Multiplies each source value by the given multiplier, then adds it to the destination value. */ static void JUCE_CALLTYPE addWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept; + /** Multiplies each source1 value by the corresponding source2 value, then adds it to the destination value. */ + static void JUCE_CALLTYPE addWithMultiply (float* dest, const float* src1, const float* src2, int num) noexcept; + + /** Multiplies each source1 value by the corresponding source2 value, then adds it to the destination value. */ + static void JUCE_CALLTYPE addWithMultiply (double* dest, const double* src1, const double* src2, int num) noexcept; + /** Multiplies the destination values by the source values. */ static void JUCE_CALLTYPE multiply (float* dest, const float* src, int numValues) noexcept; @@ -134,6 +140,36 @@ public: /** 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; + /** Each element of dest will be the minimum of the corresponding element of the source array and the given comp value. */ + static void JUCE_CALLTYPE min (float* dest, const float* src, float comp, int num) noexcept; + + /** Each element of dest will be the minimum of the corresponding element of the source array and the given comp value. */ + static void JUCE_CALLTYPE min (double* dest, const double* src, double comp, int num) noexcept; + + /** Each element of dest will be the minimum of the corresponding source1 and source2 values. */ + static void JUCE_CALLTYPE min (float* dest, const float* src1, const float* src2, int num) noexcept; + + /** Each element of dest will be the minimum of the corresponding source1 and source2 values. */ + static void JUCE_CALLTYPE min (double* dest, const double* src1, const double* src2, int num) noexcept; + + /** Each element of dest will be the maximum of the corresponding element of the source array and the given comp value. */ + static void JUCE_CALLTYPE max (float* dest, const float* src, float comp, int num) noexcept; + + /** Each element of dest will be the maximum of the corresponding element of the source array and the given comp value. */ + static void JUCE_CALLTYPE max (double* dest, const double* src, double comp, int num) noexcept; + + /** Each element of dest will be the maximum of the corresponding source1 and source2 values. */ + static void JUCE_CALLTYPE max (float* dest, const float* src1, const float* src2, int num) noexcept; + + /** Each element of dest will be the maximum of the corresponding source1 and source2 values. */ + static void JUCE_CALLTYPE max (double* dest, const double* src1, const double* src2, int num) noexcept; + + /** Each element of dest is calculated by hard clipping the corresponding src element so that it is in the range specified by the arguments low and high. */ + static void JUCE_CALLTYPE clip (float* dest, const float* src, float low, float high, int num) noexcept; + + /** Each element of dest is calculated by hard clipping the corresponding src element so that it is in the range specified by the arguments low and high. */ + static void JUCE_CALLTYPE clip (double* dest, const double* src, double low, double high, int num) noexcept; + /** Finds the miniumum and maximum values in the given array. */ static Range JUCE_CALLTYPE findMinAndMax (const float* src, int numValues) noexcept; diff --git a/source/modules/juce_audio_basics/effects/juce_FFT.cpp b/source/modules/juce_audio_basics/effects/juce_FFT.cpp new file mode 100644 index 000000000..c230792c5 --- /dev/null +++ b/source/modules/juce_audio_basics/effects/juce_FFT.cpp @@ -0,0 +1,277 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +// (For the moment, we'll implement a few local operators for this complex class - one +// day we'll probably either have a juce complex class, or use the C++11 one) +static FFT::Complex operator+ (FFT::Complex a, FFT::Complex b) noexcept { FFT::Complex c = { a.r + b.r, a.i + b.i }; return c; } +static FFT::Complex operator- (FFT::Complex a, FFT::Complex b) noexcept { FFT::Complex c = { a.r - b.r, a.i - b.i }; return c; } +static FFT::Complex operator* (FFT::Complex a, FFT::Complex b) noexcept { FFT::Complex c = { a.r * b.r - a.i * b.i, a.r * b.i + a.i * b.r }; return c; } +static FFT::Complex& operator+= (FFT::Complex& a, FFT::Complex b) noexcept { a.r += b.r; a.i += b.i; return a; } + +//============================================================================== +struct FFT::FFTConfig +{ + FFTConfig (int sizeOfFFT, bool isInverse) + : fftSize (sizeOfFFT), inverse (isInverse), twiddleTable ((size_t) sizeOfFFT) + { + for (int i = 0; i < fftSize; ++i) + { + const double phase = (isInverse ? 2.0 : -2.0) * double_Pi * i / fftSize; + twiddleTable[i].r = (float) cos (phase); + twiddleTable[i].i = (float) sin (phase); + } + + const int root = (int) std::sqrt ((double) fftSize); + int divisor = 4, n = fftSize; + + for (int i = 0; i < numElementsInArray (factors); ++i) + { + while ((n % divisor) != 0) + { + if (divisor == 2) divisor = 3; + else if (divisor == 4) divisor = 2; + else divisor += 2; + + if (divisor > root) + divisor = n; + } + + n /= divisor; + + jassert (divisor == 1 || divisor == 2 || divisor == 4); + factors[i].radix = divisor; + factors[i].length = n; + } + } + + void perform (const Complex* input, Complex* output) const noexcept + { + perform (input, output, 1, 1, factors); + } + + const int fftSize; + const bool inverse; + + struct Factor { int radix, length; }; + Factor factors[32]; + HeapBlock twiddleTable; + + void perform (const Complex* input, Complex* output, const int stride, const int strideIn, const Factor* facs) const noexcept + { + const Factor factor (*facs++); + Complex* const originalOutput = output; + const Complex* const outputEnd = output + factor.radix * factor.length; + + if (stride == 1 && factor.radix <= 5) + { + for (int i = 0; i < factor.radix; ++i) + perform (input + stride * strideIn * i, output + i * factor.length, stride * factor.radix, strideIn, facs); + + butterfly (factor, output, stride); + return; + } + + if (factor.length == 1) + { + do + { + *output++ = *input; + input += stride * strideIn; + } + while (output < outputEnd); + } + else + { + do + { + perform (input, output, stride * factor.radix, strideIn, facs); + input += stride * strideIn; + output += factor.length; + } + while (output < outputEnd); + } + + butterfly (factor, originalOutput, stride); + } + + void butterfly (const Factor factor, Complex* data, const int stride) const noexcept + { + switch (factor.radix) + { + case 1: break; + case 2: butterfly2 (data, stride, factor.length); return; + case 4: butterfly4 (data, stride, factor.length); return; + default: jassertfalse; break; + } + + Complex* scratch = static_cast (alloca (sizeof (Complex) * (size_t) factor.radix)); + + for (int i = 0; i < factor.length; ++i) + { + for (int k = i, q1 = 0; q1 < factor.radix; ++q1) + { + scratch[q1] = data[k]; + k += factor.length; + } + + for (int k = i, q1 = 0; q1 < factor.radix; ++q1) + { + int twiddleIndex = 0; + data[k] = scratch[0]; + + for (int q = 1; q < factor.radix; ++q) + { + twiddleIndex += stride * k; + + if (twiddleIndex >= fftSize) + twiddleIndex -= fftSize; + + data[k] += scratch[q] * twiddleTable[twiddleIndex]; + } + + k += factor.length; + } + } + } + + void butterfly2 (Complex* data, const int stride, const int length) const noexcept + { + Complex* dataEnd = data + length; + const Complex* tw = twiddleTable; + + for (int i = length; --i >= 0;) + { + const Complex s (*dataEnd * *tw); + tw += stride; + *dataEnd++ = *data - s; + *data++ += s; + } + } + + void butterfly4 (Complex* data, const int stride, const int length) const noexcept + { + const int lengthX2 = length * 2; + const int lengthX3 = length * 3; + + const Complex* twiddle1 = twiddleTable; + const Complex* twiddle2 = twiddle1; + const Complex* twiddle3 = twiddle1; + + for (int i = length; --i >= 0;) + { + const Complex s0 = data[length] * *twiddle1; + const Complex s1 = data[lengthX2] * *twiddle2; + const Complex s2 = data[lengthX3] * *twiddle3; + const Complex s3 = s0 + s2; + const Complex s4 = s0 - s2; + const Complex s5 = *data - s1; + *data += s1; + data[lengthX2] = *data - s3; + twiddle1 += stride; + twiddle2 += stride * 2; + twiddle3 += stride * 3; + *data += s3; + + if (inverse) + { + data[length].r = s5.r - s4.i; + data[length].i = s5.i + s4.r; + data[lengthX3].r = s5.r + s4.i; + data[lengthX3].i = s5.i - s4.r; + } + else + { + data[length].r = s5.r + s4.i; + data[length].i = s5.i - s4.r; + data[lengthX3].r = s5.r - s4.i; + data[lengthX3].i = s5.i + s4.r; + } + + ++data; + } + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FFTConfig) +}; + + +//============================================================================== +FFT::FFT (int order, bool inverse) : config (new FFTConfig (1 << order, inverse)), size (1 << order) {} +FFT::~FFT() {} + +void FFT::perform (const Complex* const input, Complex* const output) const noexcept +{ + config->perform (input, output); +} + +void FFT::performRealOnlyForwardTransform (float* d) const noexcept +{ + // This can only be called on an FFT object that was created to do forward transforms. + jassert (! config->inverse); + + Complex* const scratch = static_cast (alloca (16 + sizeof (Complex) * (size_t) size)); + + for (int i = 0; i < size; ++i) + { + scratch[i].r = d[i]; + scratch[i].i = 0; + } + + perform (scratch, reinterpret_cast (d)); +} + +void FFT::performRealOnlyInverseTransform (float* d) const noexcept +{ + // This can only be called on an FFT object that was created to do inverse transforms. + jassert (config->inverse); + + Complex* const scratch = static_cast (alloca (16 + sizeof (Complex) * (size_t) size)); + + perform (reinterpret_cast (d), scratch); + + const float scaleFactor = 1.0f / size; + + for (int i = 0; i < size; ++i) + { + d[i] = scratch[i].r * scaleFactor; + d[i + size] = scratch[i].i * scaleFactor; + } +} + +void FFT::performFrequencyOnlyForwardTransform (float* d) const noexcept +{ + performRealOnlyForwardTransform (d); + const int twiceSize = size * 2; + + for (int i = 0; i < twiceSize; i += 2) + { + d[i / 2] = juce_hypot (d[i], d[i + 1]); + + if (i >= size) + { + d[i] = 0; + d[i + 1] = 0; + } + } +} diff --git a/source/modules/juce_audio_basics/effects/juce_FFT.h b/source/modules/juce_audio_basics/effects/juce_FFT.h new file mode 100644 index 000000000..e61e80969 --- /dev/null +++ b/source/modules/juce_audio_basics/effects/juce_FFT.h @@ -0,0 +1,92 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +/** + A very minimal FFT class. + + This is only a simple low-footprint implementation and isn't tuned for speed - it may + be useful for simple applications where one of the more complex FFT libraries would be + overkill. (But in the future it may end up becoming optimised of course...) + + The FFT class itself contains lookup tables, so there's some overhead in creating + one, you should create and cache an FFT object for each size/direction of transform + that you need, and re-use them to perform the actual operation. +*/ +class JUCE_API FFT +{ +public: + /** Initialises an object for performing either a forward or inverse FFT with the given size. + The the number of points the FFT will operate on will be 2 ^ order. + */ + FFT (int order, bool isInverse); + + /** Destructor. */ + ~FFT(); + + /** A complex number, for the purposes of the FFT class. */ + struct Complex + { + float r; /**< Real part. */ + float i; /**< Imaginary part. */ + }; + + /** Performs an out-of-place FFT, either forward or inverse depending on the mode + that was passed to this object's constructor. + + The arrays must contain at least getSize() elements. + */ + void perform (const Complex* input, Complex* output) const noexcept; + + /** Performs an in-place forward transform on a block of real data. + + The size of the array passed in must be 2 * getSize(), and the first half + should contain your raw input sample data. On return, the array will contain + complex frequency + phase data, and can be passed to performRealOnlyInverseTransform() + in order to convert it back to reals. + */ + void performRealOnlyForwardTransform (float* inputOutputData) const noexcept; + + /** Performs a reverse operation to data created in performRealOnlyForwardTransform(). + + The size of the array passed in must be 2 * getSize(), containing complex + frequency and phase data. On return, the first half of the array will contain + the reconstituted samples. + */ + void performRealOnlyInverseTransform (float* inputOutputData) const noexcept; + + /** Takes an array and simply transforms it to the frequency spectrum. + This may be handy for things like frequency displays or analysis. + */ + void performFrequencyOnlyForwardTransform (float* inputOutputData) const noexcept; + + /** Returns the number of data points that this FFT was created to work with. */ + int getSize() const noexcept { return size; } + +private: + struct FFTConfig; + ScopedPointer config; + const int size; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FFT) +}; diff --git a/source/modules/juce_audio_basics/effects/juce_IIRFilter.cpp b/source/modules/juce_audio_basics/effects/juce_IIRFilter.cpp index 908a69e0f..b961a6a98 100644 --- a/source/modules/juce_audio_basics/effects/juce_IIRFilter.cpp +++ b/source/modules/juce_audio_basics/effects/juce_IIRFilter.cpp @@ -23,7 +23,7 @@ */ #if JUCE_INTEL - #define JUCE_SNAP_TO_ZERO(n) if (! (n < -1.0e-8 || n > 1.0e-8)) n = 0; + #define JUCE_SNAP_TO_ZERO(n) if (! (n < -1.0e-8f || n > 1.0e-8f)) n = 0; #else #define JUCE_SNAP_TO_ZERO(n) #endif @@ -64,7 +64,7 @@ IIRCoefficients IIRCoefficients::makeLowPass (const double sampleRate, { jassert (sampleRate > 0); - const double n = 1.0 / tan (double_Pi * frequency / sampleRate); + const double n = 1.0 / std::tan (double_Pi * frequency / sampleRate); const double nSquared = n * n; const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared); @@ -79,7 +79,7 @@ IIRCoefficients IIRCoefficients::makeLowPass (const double sampleRate, IIRCoefficients IIRCoefficients::makeHighPass (const double sampleRate, const double frequency) noexcept { - const double n = tan (double_Pi * frequency / sampleRate); + const double n = std::tan (double_Pi * frequency / sampleRate); const double nSquared = n * n; const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared); diff --git a/source/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.cpp b/source/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.cpp index 7bab3a576..6d26cc46f 100644 --- a/source/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.cpp +++ b/source/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.cpp @@ -87,7 +87,10 @@ int LagrangeInterpolator::process (const double actualRatio, const float* in, if (numOut >= 4) { - memcpy (lastInputSamples, in + (numOut - 4), 4 * sizeof (float)); + const float* end = in + numOut; + + for (int i = 0; i < 4; ++i) + lastInputSamples[i] = *--end; } else { @@ -152,7 +155,10 @@ int LagrangeInterpolator::processAdding (const double actualRatio, const float* if (numOut >= 4) { - memcpy (lastInputSamples, in + (numOut - 4), 4 * sizeof (float)); + const float* end = in + numOut; + + for (int i = 0; i < 4; ++i) + lastInputSamples[i] = *--end; } else { diff --git a/source/modules/juce_audio_basics/effects/juce_Reverb.h b/source/modules/juce_audio_basics/effects/juce_Reverb.h index 53457a60b..c0b7596ec 100644 --- a/source/modules/juce_audio_basics/effects/juce_Reverb.h +++ b/source/modules/juce_audio_basics/effects/juce_Reverb.h @@ -82,12 +82,13 @@ public: const float dryScaleFactor = 2.0f; const float wet = newParams.wetLevel * wetScaleFactor; - wet1 = wet * (newParams.width * 0.5f + 0.5f); - wet2 = wet * (1.0f - newParams.width) * 0.5f; - dry = newParams.dryLevel * dryScaleFactor; + dryGain.setValue (newParams.dryLevel * dryScaleFactor); + wetGain1.setValue (0.5f * wet * (1.0f + newParams.width)); + wetGain2.setValue (0.5f * wet * (1.0f - newParams.width)); + gain = isFrozen (newParams.freezeMode) ? 0.0f : 0.015f; parameters = newParams; - shouldUpdateDamping = true; + updateDamping(); } //============================================================================== @@ -115,7 +116,12 @@ public: allPass[1][i].setSize ((intSampleRate * (allPassTunings[i] + stereoSpread)) / 44100); } - shouldUpdateDamping = true; + const double smoothTime = 0.01; + damping .reset (sampleRate, smoothTime); + feedback.reset (sampleRate, smoothTime); + dryGain .reset (sampleRate, smoothTime); + wetGain1.reset (sampleRate, smoothTime); + wetGain2.reset (sampleRate, smoothTime); } /** Clears the reverb's buffers. */ @@ -137,18 +143,18 @@ public: { jassert (left != nullptr && right != nullptr); - if (shouldUpdateDamping) - updateDamping(); - for (int i = 0; i < numSamples; ++i) { const float input = (left[i] + right[i]) * gain; float outL = 0, outR = 0; + const float damp = damping.getNextValue(); + const float feedbck = feedback.getNextValue(); + for (int j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel { - outL += comb[0][j].process (input); - outR += comb[1][j].process (input); + outL += comb[0][j].process (input, damp, feedbck); + outR += comb[1][j].process (input, damp, feedbck); } for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series @@ -157,6 +163,10 @@ public: outR = allPass[1][j].process (outR); } + const float dry = dryGain.getNextValue(); + const float wet1 = wetGain1.getNextValue(); + const float wet2 = wetGain2.getNextValue(); + left[i] = outL * wet1 + outR * wet2 + left[i] * dry; right[i] = outR * wet1 + outL * wet2 + right[i] * dry; } @@ -167,32 +177,30 @@ public: { jassert (samples != nullptr); - if (shouldUpdateDamping) - updateDamping(); - for (int i = 0; i < numSamples; ++i) { const float input = samples[i] * gain; float output = 0; + const float damp = damping.getNextValue(); + const float feedbck = feedback.getNextValue(); + for (int j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel - output += comb[0][j].process (input); + output += comb[0][j].process (input, damp, feedbck); for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series output = allPass[0][j].process (output); + const float dry = dryGain.getNextValue(); + const float wet1 = wetGain1.getNextValue(); + samples[i] = output * wet1 + samples[i] * dry; } } private: //============================================================================== - Parameters parameters; - - volatile bool shouldUpdateDamping; - float gain, wet1, wet2, dry; - - inline static bool isFrozen (const float freezeMode) noexcept { return freezeMode >= 0.5f; } + static bool isFrozen (const float freezeMode) noexcept { return freezeMode >= 0.5f; } void updateDamping() noexcept { @@ -200,8 +208,6 @@ private: const float roomOffset = 0.7f; const float dampScaleFactor = 0.4f; - shouldUpdateDamping = false; - if (isFrozen (parameters.freezeMode)) setDamping (0.0f, 1.0f); else @@ -211,19 +217,15 @@ private: void setDamping (const float dampingToUse, const float roomSizeToUse) noexcept { - for (int j = 0; j < numChannels; ++j) - for (int i = numCombs; --i >= 0;) - comb[j][i].setFeedbackAndDamp (roomSizeToUse, dampingToUse); + damping.setValue (dampingToUse); + feedback.setValue (roomSizeToUse); } //============================================================================== class CombFilter { public: - CombFilter() noexcept - : bufferSize (0), bufferIndex (0), - feedback (0), last (0), damp1 (0), damp2 (0) - {} + CombFilter() noexcept : bufferSize (0), bufferIndex (0), last (0) {} void setSize (const int size) { @@ -243,22 +245,15 @@ private: buffer.clear ((size_t) bufferSize); } - void setFeedbackAndDamp (const float f, const float d) noexcept - { - damp1 = d; - damp2 = 1.0f - d; - feedback = f; - } - - inline float process (const float input) noexcept + float process (const float input, const float damp, const float feedbackLevel) noexcept { - const float output = buffer [bufferIndex]; - last = (output * damp2) + (last * damp1); + const float output = buffer[bufferIndex]; + last = (output * (1.0f - damp)) + (last * damp); JUCE_UNDENORMALISE (last); - float temp = input + (last * feedback); + float temp = input + (last * feedbackLevel); JUCE_UNDENORMALISE (temp); - buffer [bufferIndex] = temp; + buffer[bufferIndex] = temp; bufferIndex = (bufferIndex + 1) % bufferSize; return output; } @@ -266,7 +261,7 @@ private: private: HeapBlock buffer; int bufferSize, bufferIndex; - float feedback, last, damp1, damp2; + float last; JUCE_DECLARE_NON_COPYABLE (CombFilter) }; @@ -294,7 +289,7 @@ private: buffer.clear ((size_t) bufferSize); } - inline float process (const float input) noexcept + float process (const float input) noexcept { const float bufferedValue = buffer [bufferIndex]; float temp = input + (bufferedValue * 0.5f); @@ -311,11 +306,65 @@ private: JUCE_DECLARE_NON_COPYABLE (AllPassFilter) }; + //============================================================================== + class LinearSmoothedValue + { + public: + LinearSmoothedValue() noexcept + : currentValue (0), target (0), step (0), countdown (0), stepsToTarget (0) + { + } + + void reset (double sampleRate, double fadeLengthSeconds) noexcept + { + jassert (sampleRate > 0 && fadeLengthSeconds >= 0); + stepsToTarget = (int) std::floor (fadeLengthSeconds * sampleRate); + currentValue = target; + countdown = 0; + } + + void setValue (float newValue) noexcept + { + if (target != newValue) + { + target = newValue; + countdown = stepsToTarget; + + if (countdown <= 0) + currentValue = target; + else + step = (target - currentValue) / countdown; + } + } + + float getNextValue() noexcept + { + if (countdown <= 0) + return target; + + --countdown; + currentValue += step; + return currentValue; + } + + private: + float currentValue, target, step; + int countdown, stepsToTarget; + + JUCE_DECLARE_NON_COPYABLE (LinearSmoothedValue) + }; + + //============================================================================== enum { numCombs = 8, numAllPasses = 4, numChannels = 2 }; + Parameters parameters; + float gain; + CombFilter comb [numChannels][numCombs]; AllPassFilter allPass [numChannels][numAllPasses]; + LinearSmoothedValue damping, feedback, dryGain, wetGain1, wetGain2; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Reverb) }; diff --git a/source/modules/juce_audio_basics/juce_audio_basics.cpp b/source/modules/juce_audio_basics/juce_audio_basics.cpp index 685e7c4c2..a2ce4047d 100644 --- a/source/modules/juce_audio_basics/juce_audio_basics.cpp +++ b/source/modules/juce_audio_basics/juce_audio_basics.cpp @@ -78,6 +78,7 @@ namespace juce #include "effects/juce_IIRFilter.cpp" #include "effects/juce_IIRFilterOld.cpp" #include "effects/juce_LagrangeInterpolator.cpp" +#include "effects/juce_FFT.cpp" #include "midi/juce_MidiBuffer.cpp" #include "midi/juce_MidiFile.cpp" #include "midi/juce_MidiKeyboardState.cpp" diff --git a/source/modules/juce_audio_basics/juce_audio_basics.h b/source/modules/juce_audio_basics/juce_audio_basics.h index c9ce9abb2..65541263f 100644 --- a/source/modules/juce_audio_basics/juce_audio_basics.h +++ b/source/modules/juce_audio_basics/juce_audio_basics.h @@ -31,6 +31,9 @@ namespace juce { +#undef Complex // apparently some C libraries actually define these symbols (!) +#undef Factor + #include "buffers/juce_AudioDataConverters.h" #include "buffers/juce_AudioSampleBuffer.h" #include "buffers/juce_FloatVectorOperations.h" @@ -38,6 +41,7 @@ namespace juce #include "effects/juce_IIRFilter.h" #include "effects/juce_IIRFilterOld.h" #include "effects/juce_LagrangeInterpolator.h" +#include "effects/juce_FFT.h" #include "effects/juce_Reverb.h" #include "midi/juce_MidiMessage.h" #include "midi/juce_MidiBuffer.h" diff --git a/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp b/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp index 94a6b9bcf..51efd7f1f 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp +++ b/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp @@ -276,14 +276,11 @@ void MidiMessageSequence::deleteSysExMessages() } //============================================================================== -void MidiMessageSequence::createControllerUpdatesForTime (const int channelNumber, - const double time, - OwnedArray& dest) +void MidiMessageSequence::createControllerUpdatesForTime (const int channelNumber, const double time, Array& dest) { bool doneProg = false; bool donePitchWheel = false; - Array doneControllers; - doneControllers.ensureStorageAllocated (32); + bool doneControllers[128] = { 0 }; for (int i = list.size(); --i >= 0;) { @@ -291,28 +288,25 @@ void MidiMessageSequence::createControllerUpdatesForTime (const int channelNumbe if (mm.isForChannel (channelNumber) && mm.getTimeStamp() <= time) { - if (mm.isProgramChange()) + if (mm.isProgramChange() && ! doneProg) { - if (! doneProg) - { - dest.add (new MidiMessage (mm, 0.0)); - doneProg = true; - } + doneProg = true; + dest.add (MidiMessage (mm, 0.0)); } - else if (mm.isController()) + else if (mm.isPitchWheel() && ! donePitchWheel) { - if (! doneControllers.contains (mm.getControllerNumber())) - { - dest.add (new MidiMessage (mm, 0.0)); - doneControllers.add (mm.getControllerNumber()); - } + donePitchWheel = true; + dest.add (MidiMessage (mm, 0.0)); } - else if (mm.isPitchWheel()) + else if (mm.isController()) { - if (! donePitchWheel) + const int controllerNumber = mm.getControllerNumber(); + jassert (isPositiveAndBelow (controllerNumber, 128)); + + if (! doneControllers[controllerNumber]) { - dest.add (new MidiMessage (mm, 0.0)); - donePitchWheel = true; + doneControllers[controllerNumber] = true; + dest.add (MidiMessage (mm, 0.0)); } } } diff --git a/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h b/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h index b62940c2d..fe11dd1ba 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h +++ b/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h @@ -247,7 +247,7 @@ public: state at the required time. */ void createControllerUpdatesForTime (int channelNumber, double time, - OwnedArray& resultMessages); + Array& resultMessages); //============================================================================== /** Swaps this sequence with another one. */ diff --git a/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp b/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp index 6a0704e0f..0ff01211a 100644 --- a/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp +++ b/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp @@ -24,14 +24,14 @@ ResamplingAudioSource::ResamplingAudioSource (AudioSource* const inputSource, const bool deleteInputWhenDeleted, - const int numChannels_) + const int channels) : input (inputSource, deleteInputWhenDeleted), ratio (1.0), lastRatio (1.0), bufferPos (0), sampsInBuffer (0), subSampleOffset (0), - numChannels (numChannels_) + numChannels (channels) { jassert (input != nullptr); zeromem (coefficients, sizeof (coefficients)); @@ -51,9 +51,10 @@ void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected, double s { const SpinLock::ScopedLockType sl (ratioLock); - input->prepareToPlay (samplesPerBlockExpected, sampleRate); + const int scaledBlockSize = roundToInt (samplesPerBlockExpected * ratio); + input->prepareToPlay (scaledBlockSize, sampleRate * ratio); - buffer.setSize (numChannels, roundToInt (samplesPerBlockExpected * ratio) + 32); + buffer.setSize (numChannels, scaledBlockSize + 32); filterStates.calloc ((size_t) numChannels); srcBuffers.calloc ((size_t) numChannels); @@ -93,7 +94,7 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf lastRatio = localRatio; } - const int sampsNeeded = roundToInt (info.numSamples * localRatio) + 2; + const int sampsNeeded = roundToInt (info.numSamples * localRatio) + 3; int bufferSize = buffer.getNumSamples(); @@ -138,8 +139,11 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf } int nextPos = (bufferPos + 1) % bufferSize; + for (int m = info.numSamples; --m >= 0;) { + jassert (sampsInBuffer > 0 && nextPos != endOfBufferPos); + const float alpha = (float) subSampleOffset; for (int channel = 0; channel < channelsToProcess; ++channel) @@ -148,8 +152,6 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf subSampleOffset += localRatio; - jassert (sampsInBuffer > 0); - while (subSampleOffset >= 1.0) { if (++bufferPos >= bufferSize) diff --git a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp index 6a889b4b3..45f2d6663 100644 --- a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp +++ b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp @@ -29,6 +29,7 @@ SynthesiserSound::~SynthesiserSound() {} SynthesiserVoice::SynthesiserVoice() : currentSampleRate (44100.0), currentlyPlayingNote (-1), + currentPlayingMidiChannel (0), noteOnTime (0), keyIsDown (false), sostenutoPedalDown (false) @@ -41,8 +42,7 @@ SynthesiserVoice::~SynthesiserVoice() bool SynthesiserVoice::isPlayingChannel (const int midiChannel) const { - return currentlyPlayingSound != nullptr - && currentlyPlayingSound->appliesToChannel (midiChannel); + return currentPlayingMidiChannel == midiChannel; } void SynthesiserVoice::setCurrentPlaybackSampleRate (const double newRate) @@ -50,10 +50,16 @@ void SynthesiserVoice::setCurrentPlaybackSampleRate (const double newRate) currentSampleRate = newRate; } +bool SynthesiserVoice::isVoiceActive() const +{ + return getCurrentlyPlayingNote() >= 0; +} + void SynthesiserVoice::clearCurrentNote() { currentlyPlayingNote = -1; currentlyPlayingSound = nullptr; + currentPlayingMidiChannel = 0; } void SynthesiserVoice::aftertouchChanged (int) {} @@ -253,14 +259,15 @@ void Synthesiser::startVoice (SynthesiserVoice* const voice, if (voice->currentlyPlayingSound != nullptr) voice->stopNote (0.0f, false); - voice->startNote (midiNoteNumber, velocity, sound, - lastPitchWheelValues [midiChannel - 1]); - voice->currentlyPlayingNote = midiNoteNumber; + voice->currentPlayingMidiChannel = midiChannel; voice->noteOnTime = ++lastNoteOnCounter; voice->currentlyPlayingSound = sound; voice->keyIsDown = true; voice->sostenutoPedalDown = false; + + voice->startNote (midiNoteNumber, velocity, sound, + lastPitchWheelValues [midiChannel - 1]); } } @@ -285,7 +292,8 @@ void Synthesiser::noteOff (const int midiChannel, { SynthesiserVoice* const voice = voices.getUnchecked (i); - if (voice->getCurrentlyPlayingNote() == midiNoteNumber) + if (voice->getCurrentlyPlayingNote() == midiNoteNumber + && voice->isPlayingChannel (midiChannel)) { if (SynthesiserSound* const sound = voice->getCurrentlyPlayingSound()) { @@ -426,7 +434,7 @@ SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, { SynthesiserVoice* const voice = voices.getUnchecked (i); - if (voice->getCurrentlyPlayingNote() < 0 && voice->canPlaySound (soundToPlay)) + if ((! voice->isVoiceActive()) && voice->canPlaySound (soundToPlay)) return voice; } @@ -440,7 +448,7 @@ struct VoiceAgeSorter { static int compareElements (SynthesiserVoice* v1, SynthesiserVoice* v2) noexcept { - return v1->wasStartedBefore (*v2) ? 1 : (v2->wasStartedBefore (*v1) ? -1 : 0); + return v1->wasStartedBefore (*v2) ? -1 : (v2->wasStartedBefore (*v1) ? 1 : 0); } }; @@ -473,10 +481,10 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, } } - jassert (bottom != nullptr && top != nullptr); + const int stealableVoiceRange = usableVoices.size() - 6; // The oldest note that's playing with the target pitch playing is ideal.. - for (int i = 0; i < usableVoices.size(); ++i) + for (int i = 0; i < stealableVoiceRange; ++i) { SynthesiserVoice* const voice = usableVoices.getUnchecked (i); @@ -485,7 +493,7 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, } // ..otherwise, look for the oldest note that isn't the top or bottom note.. - for (int i = 0; i < usableVoices.size(); ++i) + for (int i = 0; i < stealableVoiceRange; ++i) { SynthesiserVoice* const voice = usableVoices.getUnchecked (i); @@ -493,6 +501,6 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, return voice; } - // ..otherwise, there's only one or two voices to choose from - we'll return the top one.. - return top; + // ..otherwise, there's only one or two voices to choose from - we'll return the oldest one.. + return usableVoices.getFirst(); } diff --git a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h index 6a32cedaa..66f09609b 100644 --- a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h +++ b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h @@ -140,6 +140,12 @@ public: */ virtual void stopNote (float velocity, bool allowTailOff) = 0; + /** Returns true if this voice is currently busy playing a sound. + By default this just checks the getCurrentlyPlayingNote() value, but can + be overridden for more advanced checking. + */ + virtual bool isVoiceActive() const; + /** Called to let the voice know that the pitch wheel has been moved. This will be called during the rendering callback, so must be fast and thread-safe. */ @@ -185,17 +191,17 @@ public: */ virtual void setCurrentPlaybackSampleRate (double newRate); - /** Returns the current target sample rate at which rendering is being done. - Subclasses may need to know this so that they can pitch things correctly. - */ - double getSampleRate() const noexcept { return currentSampleRate; } - /** Returns true if the voice is currently playing a sound which is mapped to the given midi channel. If it's not currently playing, this will return false. */ - bool isPlayingChannel (int midiChannel) const; + virtual bool isPlayingChannel (int midiChannel) const; + + /** Returns the current target sample rate at which rendering is being done. + Subclasses may need to know this so that they can pitch things correctly. + */ + double getSampleRate() const noexcept { return currentSampleRate; } /** Returns true if the key that triggered this voice is still held down. Note that the voice may still be playing after the key was released (e.g because the @@ -230,7 +236,7 @@ private: friend class Synthesiser; double currentSampleRate; - int currentlyPlayingNote; + int currentlyPlayingNote, currentPlayingMidiChannel; uint32 noteOnTime; SynthesiserSound::Ptr currentlyPlayingSound; bool keyIsDown, sostenutoPedalDown; diff --git a/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp b/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp index 79a1b51c4..e0b66146b 100644 --- a/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp +++ b/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp @@ -92,7 +92,6 @@ AudioDeviceManager::AudioDeviceManager() : numInputChansNeeded (0), numOutputChansNeeded (2), listNeedsScanning (true), - useInputNames (false), inputLevel (0), testSoundPosition (0), cpuUsageMs (0), @@ -154,7 +153,8 @@ static void addIfNotNull (OwnedArray& list, AudioIODeviceType void AudioDeviceManager::createAudioDeviceTypes (OwnedArray& list) { - addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI()); + addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (false)); + addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (true)); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_DirectSound()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_ASIO()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_CoreAudio()); @@ -177,6 +177,17 @@ void AudioDeviceManager::addAudioDeviceType (AudioIODeviceType* newDeviceType) } } +static bool deviceListContains (AudioIODeviceType* type, bool isInput, const String& name) +{ + StringArray devices (type->getDeviceNames (isInput)); + + for (int i = devices.size(); --i >= 0;) + if (devices[i].trim().equalsIgnoreCase (name.trim())) + return true; + + return false; +} + //============================================================================== String AudioDeviceManager::initialise (const int numInputChannelsNeeded, const int numOutputChannelsNeeded, @@ -363,8 +374,8 @@ AudioIODeviceType* AudioDeviceManager::findType (const String& inputName, const { AudioIODeviceType* const type = availableDeviceTypes.getUnchecked(i); - if ((inputName.isNotEmpty() && type->getDeviceNames (true).contains (inputName, true)) - || (outputName.isNotEmpty() && type->getDeviceNames (false).contains (outputName, true))) + if ((inputName.isNotEmpty() && deviceListContains (type, true, inputName)) + || (outputName.isNotEmpty() && deviceListContains (type, false, outputName))) { return type; } @@ -458,17 +469,11 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup deleteCurrentDevice(); scanDevicesIfNeeded(); - if (newOutputDeviceName.isNotEmpty() - && ! type->getDeviceNames (false).contains (newOutputDeviceName)) - { + if (newOutputDeviceName.isNotEmpty() && ! deviceListContains (type, false, newOutputDeviceName)) return "No such device: " + newOutputDeviceName; - } - if (newInputDeviceName.isNotEmpty() - && ! type->getDeviceNames (true).contains (newInputDeviceName)) - { + if (newInputDeviceName.isNotEmpty() && ! deviceListContains (type, true, newInputDeviceName)) return "No such device: " + newInputDeviceName; - } currentAudioDevice = type->createDevice (newOutputDeviceName, newInputDeviceName); diff --git a/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h b/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h index f1e483f33..639489b71 100644 --- a/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h +++ b/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h @@ -450,7 +450,6 @@ private: BigInteger inputChannels, outputChannels; ScopedPointer lastExplicitSettings; mutable bool listNeedsScanning; - bool useInputNames; Atomic inputLevelMeasurementEnabledCount; double inputLevel; ScopedPointer testSound; diff --git a/source/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp b/source/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp index 80026e7ae..bdce8f45d 100644 --- a/source/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp +++ b/source/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp @@ -50,7 +50,7 @@ AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() #endif #if ! (JUCE_WINDOWS && JUCE_WASAPI) -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI() { return nullptr; } +AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool) { return nullptr; } #endif #if ! (JUCE_WINDOWS && JUCE_DIRECTSOUND) diff --git a/source/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h b/source/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h index d6cd99a5a..debfdb22e 100644 --- a/source/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h +++ b/source/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h @@ -151,7 +151,7 @@ public: /** Creates an iOS device type if it's available on this platform, or returns null. */ static AudioIODeviceType* createAudioIODeviceType_iOSAudio(); /** Creates a WASAPI device type if it's available on this platform, or returns null. */ - static AudioIODeviceType* createAudioIODeviceType_WASAPI(); + static AudioIODeviceType* createAudioIODeviceType_WASAPI (bool exclusiveMode); /** Creates a DirectSound device type if it's available on this platform, or returns null. */ static AudioIODeviceType* createAudioIODeviceType_DirectSound(); /** Creates an ASIO device type if it's available on this platform, or returns null. */ diff --git a/source/modules/juce_audio_devices/juce_audio_devices.cpp b/source/modules/juce_audio_devices/juce_audio_devices.cpp index a9733db06..d35e6608e 100644 --- a/source/modules/juce_audio_devices/juce_audio_devices.cpp +++ b/source/modules/juce_audio_devices/juce_audio_devices.cpp @@ -72,7 +72,7 @@ This means that anyone who wants to use JUCE's ASIO abilities will have to: 1) Agree to Steinberg's licensing terms and download the ASIO SDK - (see www.steinberg.net/Steinberg/Developers.asp). + (see http://www.steinberg.net/en/company/developers.html). 2) Enable this code with a global definition #define JUCE_ASIO 1. diff --git a/source/modules/juce_audio_devices/juce_audio_devices.h b/source/modules/juce_audio_devices/juce_audio_devices.h index b8528d5bc..fd63c6ff7 100644 --- a/source/modules/juce_audio_devices/juce_audio_devices.h +++ b/source/modules/juce_audio_devices/juce_audio_devices.h @@ -43,12 +43,21 @@ #endif /** Config: JUCE_WASAPI - Enables WASAPI audio devices (Windows Vista and above). + Enables WASAPI audio devices (Windows Vista and above). See also the + JUCE_WASAPI_EXCLUSIVE flag. */ #ifndef JUCE_WASAPI #define JUCE_WASAPI 1 #endif +/** Config: JUCE_WASAPI_EXCLUSIVE + Enables WASAPI audio devices in exclusive mode (Windows Vista and above). +*/ +#ifndef JUCE_WASAPI_EXCLUSIVE + #define JUCE_WASAPI_EXCLUSIVE 0 +#endif + + /** Config: JUCE_DIRECTSOUND Enables DirectSound audio (MS Windows only). */ diff --git a/source/modules/juce_audio_devices/midi_io/juce_MidiInput.h b/source/modules/juce_audio_devices/midi_io/juce_MidiInput.h index 630802085..9b238b369 100644 --- a/source/modules/juce_audio_devices/midi_io/juce_MidiInput.h +++ b/source/modules/juce_audio_devices/midi_io/juce_MidiInput.h @@ -139,7 +139,7 @@ public: //============================================================================== /** Destructor. */ - virtual ~MidiInput(); + ~MidiInput(); /** Returns the name of this device. */ const String& getName() const noexcept { return name; } @@ -158,23 +158,21 @@ public: @see stop */ - virtual void start(); + void start(); /** Stops the device running. - @see start */ - virtual void stop(); + void stop(); -protected: +private: //============================================================================== String name; void* internal; - explicit MidiInput (const String& name); + // The input objects are created with the openDevice() method. + explicit MidiInput (const String&); -private: - //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInput) }; diff --git a/source/modules/juce_audio_devices/midi_io/juce_MidiOutput.h b/source/modules/juce_audio_devices/midi_io/juce_MidiOutput.h index c668f8ab2..ff23d68b0 100644 --- a/source/modules/juce_audio_devices/midi_io/juce_MidiOutput.h +++ b/source/modules/juce_audio_devices/midi_io/juce_MidiOutput.h @@ -82,13 +82,12 @@ public: //============================================================================== /** Destructor. */ - virtual ~MidiOutput(); + ~MidiOutput(); /** Makes this device output a midi message. - @see MidiMessage */ - virtual void sendMessageNow (const MidiMessage& message); + void sendMessageNow (const MidiMessage& message); //============================================================================== /** This lets you supply a block of messages that will be sent out at some point @@ -100,7 +99,7 @@ public: This will only work if you've already started the thread with startBackgroundThread(). - A time is supplied, at which the block of messages should be sent. This time uses + A time is specified, at which the block of messages should be sent. This time uses the same time base as Time::getMillisecondCounter(), and must be in the future. The samplesPerSecondForBuffer parameter indicates the number of samples per second @@ -108,38 +107,34 @@ public: samplesPerSecondForBuffer value is needed to convert this sample position to a real time. */ - virtual void sendBlockOfMessages (const MidiBuffer& buffer, - double millisecondCounterToStartAt, - double samplesPerSecondForBuffer); + void sendBlockOfMessages (const MidiBuffer& buffer, + double millisecondCounterToStartAt, + double samplesPerSecondForBuffer); - /** Gets rid of any midi messages that had been added by sendBlockOfMessages(). - */ - virtual void clearAllPendingMessages(); + /** Gets rid of any midi messages that had been added by sendBlockOfMessages(). */ + void clearAllPendingMessages(); /** Starts up a background thread so that the device can send blocks of data. - Call this to get the device ready, before using sendBlockOfMessages(). */ - virtual void startBackgroundThread(); + void startBackgroundThread(); /** Stops the background thread, and clears any pending midi events. - @see startBackgroundThread */ - virtual void stopBackgroundThread(); + void stopBackgroundThread(); -protected: +private: //============================================================================== void* internal; CriticalSection lock; struct PendingMessage; PendingMessage* firstMessage; - MidiOutput(); + MidiOutput(); // These objects are created with the openDevice() method. void run() override; -private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutput) }; diff --git a/source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp b/source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp index 53dc5df6b..123ab6dc0 100644 --- a/source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp +++ b/source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp @@ -47,13 +47,13 @@ public: // this is a total guess about how to calculate the latency, but seems to vaguely agree // with the devices I've tested.. YMMV - inputLatency = ((javaDevice.minBufferSizeIn * 2) / 3); - outputLatency = ((javaDevice.minBufferSizeOut * 2) / 3); + inputLatency = (javaDevice.minBufferSizeIn * 2) / 3; + outputLatency = (javaDevice.minBufferSizeOut * 2) / 3; - const int longestLatency = jmax (inputLatency, outputLatency); - const int totalLatency = inputLatency + outputLatency; - inputLatency = ((longestLatency * inputLatency) / totalLatency) & ~15; - outputLatency = ((longestLatency * outputLatency) / totalLatency) & ~15; + const int64 longestLatency = jmax (inputLatency, outputLatency); + const int64 totalLatency = inputLatency + outputLatency; + inputLatency = (int) ((longestLatency * inputLatency) / totalLatency) & ~15; + outputLatency = (int) ((longestLatency * outputLatency) / totalLatency) & ~15; } ~OpenSLAudioIODevice() diff --git a/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp b/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp index 091ffbab5..edbe361e0 100644 --- a/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp +++ b/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp @@ -30,7 +30,7 @@ namespace #endif #if JUCE_ALSA_LOGGING - #define JUCE_ALSA_LOG(dbgtext) { juce::String tempDbgBuf ("ALSA: "); tempDbgBuf << dbgtext; Logger::writeToLog (tempDbgBuf); DBG (tempDbgBuf) } + #define JUCE_ALSA_LOG(dbgtext) { juce::String tempDbgBuf ("ALSA: "); tempDbgBuf << dbgtext; Logger::writeToLog (tempDbgBuf); DBG (tempDbgBuf); } #define JUCE_CHECKED_RESULT(x) (logErrorMessage (x, __LINE__)) static int logErrorMessage (int err, int lineNum) @@ -149,7 +149,7 @@ class ALSADevice { public: ALSADevice (const String& devID, bool forInput) - : handle (0), + : handle (nullptr), bitDepth (16), numChannelsRunning (0), latency (0), @@ -183,16 +183,16 @@ public: void closeNow() { - if (handle != 0) + if (handle != nullptr) { snd_pcm_close (handle); - handle = 0; + handle = nullptr; } } bool setParameters (unsigned int sampleRate, int numChannels, int bufferSize) { - if (handle == 0) + if (handle == nullptr) return false; JUCE_ALSA_LOG ("ALSADevice::setParameters(" << deviceID << ", " @@ -644,8 +644,21 @@ public: { while (! threadShouldExit()) { - if (inputDevice != nullptr && inputDevice->handle) + if (inputDevice != nullptr && inputDevice->handle != nullptr) { + if (outputDevice == nullptr || outputDevice->handle == nullptr) + { + JUCE_ALSA_FAILED (snd_pcm_wait (inputDevice->handle, 2000)); + + if (threadShouldExit()) + break; + + snd_pcm_sframes_t avail = snd_pcm_avail_update (inputDevice->handle); + + if (avail < 0) + JUCE_ALSA_FAILED (snd_pcm_recover (inputDevice->handle, avail, 0)); + } + audioIoInProgress = true; if (! inputDevice->readFromInputDevice (inputChannelBuffer, bufferSize)) @@ -679,7 +692,7 @@ public: } } - if (outputDevice != nullptr && outputDevice->handle) + if (outputDevice != nullptr && outputDevice->handle != nullptr) { JUCE_ALSA_FAILED (snd_pcm_wait (outputDevice->handle, 2000)); @@ -702,6 +715,7 @@ public: audioIoInProgress = false; } } + audioIoInProgress = false; } diff --git a/source/modules/juce_audio_devices/native/juce_linux_Midi.cpp b/source/modules/juce_audio_devices/native/juce_linux_Midi.cpp index a6799f3d5..43cb43dc0 100644 --- a/source/modules/juce_audio_devices/native/juce_linux_Midi.cpp +++ b/source/modules/juce_audio_devices/native/juce_linux_Midi.cpp @@ -419,7 +419,7 @@ public: port.deletePort(); } - void sendMessageNow (const MidiMessage& message) + bool sendMessageNow (const MidiMessage& message) { if (message.getRawDataSize() > maxEventSize) { @@ -435,12 +435,17 @@ public: const uint8* data = message.getRawData(); snd_seq_t* seqHandle = port.client->get(); + bool success = true; while (numBytes > 0) { const long numSent = snd_midi_event_encode (midiParser, data, numBytes, &event); + if (numSent <= 0) + { + success = numSent == 0; break; + } numBytes -= numSent; data += numSent; @@ -449,11 +454,15 @@ public: snd_seq_ev_set_subs (&event); snd_seq_ev_set_direct (&event); - snd_seq_event_output (seqHandle, &event); + if (snd_seq_event_output_direct (seqHandle, &event) < 0) + { + success = false; + break; + } } - snd_seq_drain_output (seqHandle); snd_midi_event_reset_encode (midiParser); + return success; } private: diff --git a/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp b/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp index d7044eb13..0061b0fb4 100644 --- a/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp +++ b/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp @@ -543,7 +543,7 @@ public: // set sample rate AudioObjectPropertyAddress pa; pa.mSelector = kAudioDevicePropertyNominalSampleRate; - pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mScope = kAudioObjectPropertyScopeGlobal; pa.mElement = kAudioObjectPropertyElementMaster; Float64 sr = newSampleRate; diff --git a/source/modules/juce_audio_devices/native/juce_win32_ASIO.cpp b/source/modules/juce_audio_devices/native/juce_win32_ASIO.cpp index b171bedd6..99e3cfbde 100644 --- a/source/modules/juce_audio_devices/native/juce_win32_ASIO.cpp +++ b/source/modules/juce_audio_devices/native/juce_win32_ASIO.cpp @@ -123,9 +123,9 @@ struct ASIOSampleFormat { switch (bitDepth) { - case 16: convertInt16ToFloat (static_cast (src), dst, byteStride, samps, littleEndian); break; - case 24: convertInt24ToFloat (static_cast (src), dst, byteStride, samps, littleEndian); break; - case 32: convertInt32ToFloat (static_cast (src), dst, byteStride, samps, littleEndian); break; + case 16: convertInt16ToFloat (static_cast (src), dst, byteStride, samps, littleEndian); break; + case 24: convertInt24ToFloat (static_cast (src), dst, byteStride, samps, littleEndian); break; + case 32: convertInt32ToFloat (static_cast (src), dst, byteStride, samps, littleEndian); break; default: jassertfalse; break; } } @@ -141,9 +141,9 @@ struct ASIOSampleFormat { switch (bitDepth) { - case 16: convertFloatToInt16 (src, static_cast (dst), byteStride, samps, littleEndian); break; - case 24: convertFloatToInt24 (src, static_cast (dst), byteStride, samps, littleEndian); break; - case 32: convertFloatToInt32 (src, static_cast (dst), byteStride, samps, littleEndian); break; + case 16: convertFloatToInt16 (src, static_cast (dst), byteStride, samps, littleEndian); break; + case 24: convertFloatToInt24 (src, static_cast (dst), byteStride, samps, littleEndian); break; + case 32: convertFloatToInt32 (src, static_cast (dst), byteStride, samps, littleEndian); break; default: jassertfalse; break; } } @@ -300,7 +300,7 @@ private: //============================================================================== class ASIOAudioIODevice; -static ASIOAudioIODevice* volatile currentASIODev[3] = { 0 }; +static ASIOAudioIODevice* volatile currentASIODev[16] = { 0 }; extern HWND juce_messageWindowHandle; @@ -438,8 +438,6 @@ public: currentBlockSizeSamples = bufferSizeSamples; currentChansOut.clear(); currentChansIn.clear(); - inBuffers.clear (totalNumInputChans + 1); - outBuffers.clear (totalNumOutputChans + 1); updateSampleRates(); @@ -458,6 +456,13 @@ public: setSampleRate (sampleRate); + // (need to get this again in case a sample rate change affected the channel count) + err = asioObject->getChannels (&totalNumInputChans, &totalNumOutputChans); + jassert (err == ASE_OK); + + inBuffers.calloc (totalNumInputChans + 8); + outBuffers.calloc (totalNumOutputChans + 8); + if (needToReset) { JUCE_ASIO_LOG (" Resetting"); @@ -1320,7 +1325,7 @@ private: inputFormat[i].convertToFloat (infos[i].buffers[bi], inBuffers[i], samps); } - currentCallback->audioDeviceIOCallback (const_cast (inBuffers.getData()), numActiveInputChans, + currentCallback->audioDeviceIOCallback (const_cast (inBuffers.getData()), numActiveInputChans, outBuffers, numActiveOutputChans, samps); for (int i = 0; i < numActiveOutputChans; ++i) @@ -1340,6 +1345,29 @@ private: asioObject->outputReady(); } + long asioMessagesCallback (long selector, long value) + { + switch (selector) + { + case kAsioSelectorSupported: + if (value == kAsioResetRequest || value == kAsioEngineVersion || value == kAsioResyncRequest + || value == kAsioLatenciesChanged || value == kAsioSupportsInputMonitor) + return 1; + break; + + case kAsioBufferSizeChange: JUCE_ASIO_LOG ("kAsioBufferSizeChange"); resetRequest(); return 1; + case kAsioResetRequest: JUCE_ASIO_LOG ("kAsioResetRequest"); resetRequest(); return 1; + case kAsioResyncRequest: JUCE_ASIO_LOG ("kAsioResyncRequest"); resetRequest(); return 1; + case kAsioLatenciesChanged: JUCE_ASIO_LOG ("kAsioLatenciesChanged"); return 1; + case kAsioEngineVersion: return 2; + + case kAsioSupportsTimeInfo: + case kAsioSupportsTimeCode: return 0; + } + + return 0; + } + //============================================================================== template struct ASIOCallbackFunctions @@ -1360,56 +1388,43 @@ private: static long JUCE_ASIOCALLBACK asioMessagesCallback (long selector, long value, void*, double*) { - switch (selector) - { - case kAsioSelectorSupported: - if (value == kAsioResetRequest || value == kAsioEngineVersion || value == kAsioResyncRequest - || value == kAsioLatenciesChanged || value == kAsioSupportsInputMonitor) - return 1; - break; - - case kAsioBufferSizeChange: JUCE_ASIO_LOG ("kAsioBufferSizeChange"); return sendResetRequest (deviceIndex); - case kAsioResetRequest: JUCE_ASIO_LOG ("kAsioResetRequest"); return sendResetRequest (deviceIndex); - case kAsioResyncRequest: JUCE_ASIO_LOG ("kAsioResyncRequest"); return sendResetRequest (deviceIndex); - case kAsioLatenciesChanged: JUCE_ASIO_LOG ("kAsioLatenciesChanged"); return 1; - case kAsioEngineVersion: return 2; - - case kAsioSupportsTimeInfo: - case kAsioSupportsTimeCode: - return 0; - } - - return 0; + return currentASIODev[deviceIndex] != nullptr + ? currentASIODev[deviceIndex]->asioMessagesCallback (selector, value) + : 0; } static void JUCE_ASIOCALLBACK sampleRateChangedCallback (ASIOSampleRate) { - sendResetRequest (deviceIndex); + if (currentASIODev[deviceIndex] != nullptr) + currentASIODev[deviceIndex]->resetRequest(); } - static long sendResetRequest (int index) + static void setCallbacks (ASIOCallbacks& callbacks) noexcept { - if (currentASIODev[index] != nullptr) - currentASIODev[index]->resetRequest(); - - return 1; + callbacks.bufferSwitch = &bufferSwitchCallback; + callbacks.asioMessage = &asioMessagesCallback; + callbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfoCallback; + callbacks.sampleRateDidChange = &sampleRateChangedCallback; } - static void setCallbacks (ASIOCallbacks& callbacks) + static void setCallbacksForDevice (ASIOCallbacks& callbacks, ASIOAudioIODevice* device) noexcept { - callbacks.bufferSwitch = &bufferSwitchCallback; - callbacks.asioMessage = &asioMessagesCallback; - callbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfoCallback; - callbacks.sampleRateDidChange = &sampleRateChangedCallback; + if (currentASIODev[deviceIndex] == device) + setCallbacks (callbacks); + else + ASIOCallbackFunctions::setCallbacksForDevice (callbacks, device); } }; - void setCallbackFunctions() + template <> + struct ASIOCallbackFunctions + { + 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 jassertfalse; + ASIOCallbackFunctions<0>::setCallbacksForDevice (callbacks, this); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ASIOAudioIODevice) @@ -1503,15 +1518,16 @@ public: jassert (inputDeviceName == outputDeviceName || outputDeviceName.isEmpty() || inputDeviceName.isEmpty()); jassert (hasScanned); // need to call scanForDevices() before doing this - const int index = deviceNames.indexOf (outputDeviceName.isNotEmpty() ? outputDeviceName - : inputDeviceName); + const String deviceName (outputDeviceName.isNotEmpty() ? outputDeviceName + : inputDeviceName); + const int index = deviceNames.indexOf (deviceName); if (index >= 0) { const int freeSlot = findFreeSlot(); if (freeSlot >= 0) - return new ASIOAudioIODevice (this, outputDeviceName, + return new ASIOAudioIODevice (this, deviceName, classIds.getReference (index), freeSlot); } diff --git a/source/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp b/source/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp index 9c43a1da0..88a5960fe 100644 --- a/source/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp +++ b/source/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp @@ -1255,7 +1255,7 @@ private: DSoundDeviceList deviceList; bool hasScanned; - void systemDeviceChanged() + void systemDeviceChanged() override { DSoundDeviceList newList; newList.scan(); diff --git a/source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp b/source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp index 320eca1a5..875ce6192 100644 --- a/source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp +++ b/source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp @@ -45,6 +45,7 @@ void logFailure (HRESULT hr) { case E_POINTER: m = "E_POINTER"; break; case E_INVALIDARG: m = "E_INVALIDARG"; break; + case E_NOINTERFACE: m = "E_NOINTERFACE"; break; #define JUCE_WASAPI_ERR(desc, n) \ case MAKE_HRESULT(1, 0x889, n): m = #desc; break; @@ -126,7 +127,11 @@ enum EDataFlow eAll = (eCapture + 1) }; -enum { DEVICE_STATE_ACTIVE = 1 }; +enum +{ + DEVICE_STATE_ACTIVE = 1, + AUDCLNT_BUFFERFLAGS_SILENT = 2 +}; JUCE_IUNKNOWNCLASS (IPropertyStore, "886d8eeb-8cf2-4446-8d02-cdba1dbdcf99") { @@ -328,6 +333,11 @@ int refTimeToSamples (const REFERENCE_TIME& t, const double sampleRate) noexcept return roundToInt (sampleRate * ((double) t) * 0.0000001); } +REFERENCE_TIME samplesToRefTime (const int numSamples, const double sampleRate) noexcept +{ + return (REFERENCE_TIME) ((numSamples * 10000.0 * 1000.0 / sampleRate) + 0.5); +} + void copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* const src) noexcept { memcpy (&dest, src, src->wFormatTag == WAVE_FORMAT_EXTENSIBLE ? sizeof (WAVEFORMATEXTENSIBLE) @@ -348,9 +358,12 @@ public: defaultBufferSize (0), latencySamples (0), useExclusiveMode (exclusiveMode), + actualBufferSize (0), + bytesPerSample (0), + bytesPerFrame (0), sampleRateHasChanged (false) { - clientEvent = CreateEvent (0, false, false, _T("JuceWASAPI")); + clientEvent = CreateEvent (nullptr, false, false, nullptr); ComSmartPtr tempClient (createClient()); if (tempClient == nullptr) @@ -376,16 +389,24 @@ public: rates.addUsingDefaultSort (defaultSampleRate); - static const int ratesToTest[] = { 44100, 48000, 88200, 96000, 176400, 192000 }; + if (useExclusiveMode + && findSupportedFormat (tempClient, defaultSampleRate, format.dwChannelMask, format)) + { + // Got a format that is supported by the device so we can ask what sample rates are supported (in whatever format) + } + + static const int ratesToTest[] = { 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000 }; for (int i = 0; i < numElementsInArray (ratesToTest); ++i) { - if (ratesToTest[i] == defaultSampleRate) + if (rates.contains (ratesToTest[i])) continue; - format.Format.nSamplesPerSec = (DWORD) ratesToTest[i]; + format.Format.nSamplesPerSec = (DWORD) ratesToTest[i]; + format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nChannels * format.Format.wBitsPerSample / 8); - if (SUCCEEDED (tempClient->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED, + if (SUCCEEDED (tempClient->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE + : AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*) &format, 0))) if (! rates.contains (ratesToTest[i])) rates.addUsingDefaultSort (ratesToTest[i]); @@ -400,7 +421,7 @@ public: bool isOk() const noexcept { return defaultBufferSize > 0 && defaultSampleRate > 0; } - bool openClient (const double newSampleRate, const BigInteger& newChannels) + bool openClient (const double newSampleRate, const BigInteger& newChannels, const int bufferSizeSamples) { sampleRate = newSampleRate; channels = newChannels; @@ -413,8 +434,7 @@ public: client = createClient(); if (client != nullptr - && (tryInitialisingWithFormat (true, 4) || tryInitialisingWithFormat (false, 4) - || tryInitialisingWithFormat (false, 3) || tryInitialisingWithFormat (false, 2))) + && tryInitialisingWithBufferSize (bufferSizeSamples)) { sampleRateHasChanged = false; @@ -465,7 +485,7 @@ public: BigInteger channels; Array channelMaps; UINT32 actualBufferSize; - int bytesPerSample; + int bytesPerSample, bytesPerFrame; bool sampleRateHasChanged; virtual void updateFormat (bool isFloat) = 0; @@ -536,12 +556,19 @@ private: return client; } - bool tryInitialisingWithFormat (const bool useFloat, const int bytesPerSampleToTry) + struct AudioSampleFormat + { + bool useFloat; + int bitsPerSampleToTry; + int bytesPerSampleContainer; + }; + + bool tryFormat (const AudioSampleFormat sampleFormat, IAudioClient* clientToUse, double sampleRate, + DWORD mixFormatChannelMask, WAVEFORMATEXTENSIBLE& format) const { - WAVEFORMATEXTENSIBLE format; zerostruct (format); - if (numChannels <= 2 && bytesPerSampleToTry <= 2) + if (numChannels <= 2 && sampleFormat.bitsPerSampleToTry <= 16) { format.Format.wFormatTag = WAVE_FORMAT_PCM; } @@ -553,45 +580,97 @@ private: format.Format.nSamplesPerSec = (DWORD) sampleRate; format.Format.nChannels = (WORD) numChannels; - format.Format.wBitsPerSample = (WORD) (8 * bytesPerSampleToTry); - format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * numChannels * bytesPerSampleToTry); - format.Format.nBlockAlign = (WORD) (numChannels * bytesPerSampleToTry); - format.SubFormat = useFloat ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM; - format.Samples.wValidBitsPerSample = format.Format.wBitsPerSample; + format.Format.wBitsPerSample = (WORD) (8 * sampleFormat.bytesPerSampleContainer); + format.Samples.wValidBitsPerSample = (WORD) (sampleFormat.bitsPerSampleToTry); + format.Format.nBlockAlign = (WORD) (format.Format.nChannels * format.Format.wBitsPerSample / 8); + format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nBlockAlign); + format.SubFormat = sampleFormat.useFloat ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM; format.dwChannelMask = mixFormatChannelMask; WAVEFORMATEXTENSIBLE* nearestFormat = nullptr; - HRESULT hr = client->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE - : AUDCLNT_SHAREMODE_SHARED, - (WAVEFORMATEX*) &format, - useExclusiveMode ? nullptr : (WAVEFORMATEX**) &nearestFormat); + HRESULT hr = clientToUse->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE + : AUDCLNT_SHAREMODE_SHARED, + (WAVEFORMATEX*) &format, + useExclusiveMode ? nullptr : (WAVEFORMATEX**) &nearestFormat); logFailure (hr); if (hr == S_FALSE && format.Format.nSamplesPerSec == nearestFormat->Format.nSamplesPerSec) { - copyWavFormat (format, (WAVEFORMATEX*) nearestFormat); + copyWavFormat (format, (const WAVEFORMATEX*) nearestFormat); hr = S_OK; } CoTaskMemFree (nearestFormat); + return check (hr); + } - REFERENCE_TIME defaultPeriod = 0, minPeriod = 0; - if (useExclusiveMode) - check (client->GetDevicePeriod (&defaultPeriod, &minPeriod)); + bool findSupportedFormat (IAudioClient* clientToUse, double sampleRate, + DWORD mixFormatChannelMask, WAVEFORMATEXTENSIBLE& format) const + { + static const AudioSampleFormat formats[] = + { + { true, 32, 4 }, + { false, 32, 4 }, + { false, 24, 4 }, + { false, 24, 3 }, + { false, 20, 4 }, + { false, 20, 3 }, + { false, 16, 2 } + }; + + for (int i = 0; i < numElementsInArray (formats); ++i) + if (tryFormat (formats[i], clientToUse, sampleRate, mixFormatChannelMask, format)) + return true; + + return false; + } - GUID session; - if (hr == S_OK - && check (client->Initialize (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED, - 0x40000 /*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/, - defaultPeriod, defaultPeriod, (WAVEFORMATEX*) &format, &session))) + bool tryInitialisingWithBufferSize (const int bufferSizeSamples) + { + WAVEFORMATEXTENSIBLE format; + + if (findSupportedFormat (client, sampleRate, mixFormatChannelMask, format)) { - actualNumChannels = format.Format.nChannels; - const bool isFloat = format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && format.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - bytesPerSample = format.Format.wBitsPerSample / 8; + REFERENCE_TIME defaultPeriod = 0, minPeriod = 0; - updateFormat (isFloat); - return true; + check (client->GetDevicePeriod (&defaultPeriod, &minPeriod)); + + if (useExclusiveMode && bufferSizeSamples > 0) + defaultPeriod = jmax (minPeriod, samplesToRefTime (bufferSizeSamples, format.Format.nSamplesPerSec)); + + for (;;) + { + GUID session; + HRESULT hr = client->Initialize (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED, + 0x40000 /*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/, + defaultPeriod, useExclusiveMode ? defaultPeriod : 0, (WAVEFORMATEX*) &format, &session); + + if (check (hr)) + { + actualNumChannels = format.Format.nChannels; + const bool isFloat = format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && format.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + bytesPerSample = format.Format.wBitsPerSample / 8; + bytesPerFrame = format.Format.nBlockAlign; + + updateFormat (isFloat); + return true; + } + + // Handle the "alignment dance" : http://msdn.microsoft.com/en-us/library/windows/desktop/dd370875(v=vs.85).aspx (see Remarks) + if (hr != MAKE_HRESULT (1, 0x889, 0x19)) // AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED + break; + + UINT32 numFrames = 0; + if (! check (client->GetBufferSize (&numFrames))) + break; + + // Recreate client + client = nullptr; + client = createClient(); + + defaultPeriod = samplesToRefTime (numFrames, format.Format.nSamplesPerSec); + } } return false; @@ -615,12 +694,9 @@ public: close(); } - bool open (const double newSampleRate, const BigInteger& newChannels) + bool open (const double newSampleRate, const BigInteger& newChannels, int bufferSizeSamples) { - reservoirSize = 0; - reservoirCapacity = 16384; - reservoir.setSize (actualNumChannels * reservoirCapacity * sizeof (float)); - return openClient (newSampleRate, newChannels) + return openClient (newSampleRate, newChannels, bufferSizeSamples) && (numChannels == 0 || check (client->GetService (__uuidof (IAudioCaptureClient), (void**) captureClient.resetAndGetPointerAddress()))); } @@ -630,88 +706,129 @@ public: closeClient(); captureClient = nullptr; reservoir.reset(); + reservoirReadPos = reservoirWritePos = 0; } template - void updateFormatWithType (SourceType*) + void updateFormatWithType (SourceType*) noexcept { typedef AudioData::Pointer NativeType; converter = new AudioData::ConverterInstance, NativeType> (actualNumChannels, 1); } - void updateFormat (bool isFloat) + void updateFormat (bool isFloat) override + { + if (isFloat) updateFormatWithType ((AudioData::Float32*) nullptr); + else if (bytesPerSample == 4) updateFormatWithType ((AudioData::Int32*) nullptr); + else if (bytesPerSample == 3) updateFormatWithType ((AudioData::Int24*) nullptr); + else updateFormatWithType ((AudioData::Int16*) nullptr); + } + + bool start (const int userBufferSize) + { + reservoirSize = actualBufferSize + userBufferSize; + reservoirMask = nextPowerOfTwo (reservoirSize) - 1; + reservoir.setSize ((reservoirMask + 1) * bytesPerFrame, true); + reservoirReadPos = reservoirWritePos = 0; + + if (! check (client->Start())) + return false; + + purgeInputBuffers(); + return true; + } + + void purgeInputBuffers() { - if (isFloat) updateFormatWithType ((AudioData::Float32*) 0); - else if (bytesPerSample == 4) updateFormatWithType ((AudioData::Int32*) 0); - else if (bytesPerSample == 3) updateFormatWithType ((AudioData::Int24*) 0); - else updateFormatWithType ((AudioData::Int16*) 0); + uint8* inputData; + UINT32 numSamplesAvailable; + DWORD flags; + + while (captureClient->GetBuffer (&inputData, &numSamplesAvailable, &flags, nullptr, nullptr) + != MAKE_HRESULT (0, 0x889, 0x1) /* AUDCLNT_S_BUFFER_EMPTY */) + captureClient->ReleaseBuffer (numSamplesAvailable); } - void copyBuffers (float** destBuffers, int numDestBuffers, int bufferSize, Thread& thread) + int getNumSamplesInReservoir() const noexcept { return reservoirWritePos - reservoirReadPos; } + + void handleDeviceBuffer() { if (numChannels <= 0) return; - int offset = 0; + uint8* inputData; + UINT32 numSamplesAvailable; + DWORD flags; - while (bufferSize > 0) + while (check (captureClient->GetBuffer (&inputData, &numSamplesAvailable, &flags, nullptr, nullptr)) && numSamplesAvailable > 0) { - if (reservoirSize > 0) // There's stuff in the reservoir, so use that... + int samplesLeft = (int) numSamplesAvailable; + + while (samplesLeft > 0) { - const int samplesToDo = jmin (bufferSize, (int) reservoirSize); + const int localWrite = reservoirWritePos & reservoirMask; + const int samplesToDo = jmin (samplesLeft, reservoirMask + 1 - localWrite); + const int samplesToDoBytes = samplesToDo * bytesPerFrame; - for (int i = 0; i < numDestBuffers; ++i) - converter->convertSamples (destBuffers[i] + offset, 0, reservoir.getData(), channelMaps.getUnchecked(i), samplesToDo); + void* reservoirPtr = addBytesToPointer (reservoir.getData(), localWrite * bytesPerFrame); - bufferSize -= samplesToDo; - offset += samplesToDo; - reservoirSize = 0; + if ((flags & AUDCLNT_BUFFERFLAGS_SILENT) != 0) + zeromem (reservoirPtr, samplesToDoBytes); + else + memcpy (reservoirPtr, inputData, samplesToDoBytes); + + reservoirWritePos += samplesToDo; + inputData += samplesToDoBytes; + samplesLeft -= samplesToDo; } - else - { - UINT32 packetLength = 0; - if (! check (captureClient->GetNextPacketSize (&packetLength))) - break; - if (packetLength == 0) - { - if (thread.threadShouldExit() - || WaitForSingleObject (clientEvent, 1000) == WAIT_TIMEOUT) - break; + if (getNumSamplesInReservoir() > reservoirSize) + reservoirReadPos = reservoirWritePos - reservoirSize; - continue; - } + captureClient->ReleaseBuffer (numSamplesAvailable); + } + } - uint8* inputData; - UINT32 numSamplesAvailable; - DWORD flags; + void copyBuffersFromReservoir (float** destBuffers, int numDestBuffers, int bufferSize) + { + if ((numChannels <= 0 && bufferSize == 0) || reservoir.getSize() == 0) + return; - if (check (captureClient->GetBuffer (&inputData, &numSamplesAvailable, &flags, 0, 0))) - { - const int samplesToDo = jmin (bufferSize, (int) numSamplesAvailable); + int offset = jmax (0, bufferSize - getNumSamplesInReservoir()); - for (int i = 0; i < numDestBuffers; ++i) - converter->convertSamples (destBuffers[i] + offset, 0, inputData, channelMaps.getUnchecked(i), samplesToDo); + if (offset > 0) + { + for (int i = 0; i < numDestBuffers; ++i) + zeromem (destBuffers[i], offset * sizeof (float)); - bufferSize -= samplesToDo; - offset += samplesToDo; + bufferSize -= offset; + reservoirReadPos -= offset / 2; + } - if (samplesToDo < (int) numSamplesAvailable) - { - reservoirSize = jmin ((int) (numSamplesAvailable - samplesToDo), reservoirCapacity); - memcpy ((uint8*) reservoir.getData(), inputData + bytesPerSample * actualNumChannels * samplesToDo, - (size_t) (bytesPerSample * actualNumChannels * reservoirSize)); - } + while (bufferSize > 0) + { + const int localRead = reservoirReadPos & reservoirMask; - captureClient->ReleaseBuffer (numSamplesAvailable); - } - } + const int samplesToDo = jmin (bufferSize, getNumSamplesInReservoir(), reservoirMask + 1 - localRead); + if (samplesToDo <= 0) + break; + + const int reservoirOffset = localRead * bytesPerFrame; + + for (int i = 0; i < numDestBuffers; ++i) + converter->convertSamples (destBuffers[i] + offset, 0, addBytesToPointer (reservoir.getData(), reservoirOffset), channelMaps.getUnchecked(i), samplesToDo); + + bufferSize -= samplesToDo; + offset += samplesToDo; + reservoirReadPos += samplesToDo; } } ComSmartPtr captureClient; MemoryBlock reservoir; - int reservoirSize, reservoirCapacity; + int reservoirSize, reservoirMask; + volatile int reservoirReadPos, reservoirWritePos; + ScopedPointer converter; private: @@ -732,10 +849,11 @@ public: close(); } - bool open (const double newSampleRate, const BigInteger& newChannels) + bool open (const double newSampleRate, const BigInteger& newChannels, int bufferSizeSamples) { - return openClient (newSampleRate, newChannels) - && (numChannels == 0 || check (client->GetService (__uuidof (IAudioRenderClient), (void**) renderClient.resetAndGetPointerAddress()))); + return openClient (newSampleRate, newChannels, bufferSizeSamples) + && (numChannels == 0 || check (client->GetService (__uuidof (IAudioRenderClient), + (void**) renderClient.resetAndGetPointerAddress()))); } void close() @@ -751,15 +869,42 @@ public: converter = new AudioData::ConverterInstance > (1, actualNumChannels); } - void updateFormat (bool isFloat) + void updateFormat (bool isFloat) override + { + if (isFloat) updateFormatWithType ((AudioData::Float32*) nullptr); + else if (bytesPerSample == 4) updateFormatWithType ((AudioData::Int32*) nullptr); + else if (bytesPerSample == 3) updateFormatWithType ((AudioData::Int24*) nullptr); + else updateFormatWithType ((AudioData::Int16*) nullptr); + } + + bool start() { - if (isFloat) updateFormatWithType ((AudioData::Float32*) 0); - else if (bytesPerSample == 4) updateFormatWithType ((AudioData::Int32*) 0); - else if (bytesPerSample == 3) updateFormatWithType ((AudioData::Int24*) 0); - else updateFormatWithType ((AudioData::Int16*) 0); + int samplesToDo = getNumSamplesAvailableToCopy(); + uint8* outputData; + + if (check (renderClient->GetBuffer (samplesToDo, &outputData))) + renderClient->ReleaseBuffer (samplesToDo, AUDCLNT_BUFFERFLAGS_SILENT); + + return check (client->Start()); + } + + int getNumSamplesAvailableToCopy() const + { + if (numChannels <= 0) + return 0; + + if (! useExclusiveMode) + { + UINT32 padding = 0; + if (check (client->GetCurrentPadding (&padding))) + return actualBufferSize - (int) padding; + } + + return actualBufferSize; } - void copyBuffers (const float** const srcBuffers, const int numSrcBuffers, int bufferSize, Thread& thread) + void copyBuffers (const float** const srcBuffers, const int numSrcBuffers, int bufferSize, + WASAPIInputDevice* inputDevice, Thread& thread) { if (numChannels <= 0) return; @@ -768,22 +913,25 @@ public: while (bufferSize > 0) { - UINT32 padding = 0; - if (! check (client->GetCurrentPadding (&padding))) - return; + // This is needed in order not to drop any input data if the output device endpoint buffer was full + if ((! useExclusiveMode) && inputDevice != nullptr + && WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0) + inputDevice->handleDeviceBuffer(); - int samplesToDo = useExclusiveMode ? bufferSize - : jmin ((int) (actualBufferSize - padding), bufferSize); + int samplesToDo = jmin (getNumSamplesAvailableToCopy(), bufferSize); - if (samplesToDo <= 0) + if (samplesToDo == 0) { - if (thread.threadShouldExit() - || WaitForSingleObject (clientEvent, 1000) == WAIT_TIMEOUT) - break; + // This can ONLY occur in non-exclusive mode + if (! thread.threadShouldExit() && WaitForSingleObject (clientEvent, 1000) == WAIT_OBJECT_0) + continue; - continue; + break; } + if (useExclusiveMode && WaitForSingleObject (clientEvent, 1000) == WAIT_TIMEOUT) + break; + uint8* outputData = nullptr; if (check (renderClient->GetBuffer ((UINT32) samplesToDo, &outputData))) { @@ -791,10 +939,10 @@ public: converter->convertSamples (outputData, channelMaps.getUnchecked(i), srcBuffers[i] + offset, 0, samplesToDo); renderClient->ReleaseBuffer ((UINT32) samplesToDo, 0); - - offset += samplesToDo; - bufferSize -= samplesToDo; } + + bufferSize -= samplesToDo; + offset += samplesToDo; } } @@ -812,13 +960,14 @@ class WASAPIAudioIODevice : public AudioIODevice, { public: WASAPIAudioIODevice (const String& deviceName, - const String& outputDeviceId_, - const String& inputDeviceId_, + const String& typeName, + const String& outputDeviceID, + const String& inputDeviceID, const bool exclusiveMode) - : AudioIODevice (deviceName, "Windows Audio"), + : AudioIODevice (deviceName, typeName), Thread ("Juce WASAPI"), - outputDeviceId (outputDeviceId_), - inputDeviceId (inputDeviceId_), + outputDeviceId (outputDeviceID), + inputDeviceId (inputDeviceID), useExclusiveMode (exclusiveMode), isOpen_ (false), isStarted (false), @@ -932,19 +1081,33 @@ public: lastKnownInputChannels = inputChannels; lastKnownOutputChannels = outputChannels; - if (inputDevice != nullptr && ! inputDevice->open (currentSampleRate, inputChannels)) + if (inputDevice != nullptr && ! inputDevice->open (currentSampleRate, inputChannels, bufferSizeSamples)) { lastError = TRANS("Couldn't open the input device!"); return lastError; } - if (outputDevice != nullptr && ! outputDevice->open (currentSampleRate, outputChannels)) + if (outputDevice != nullptr && ! outputDevice->open (currentSampleRate, outputChannels, bufferSizeSamples)) { close(); lastError = TRANS("Couldn't open the output device!"); return lastError; } + if (useExclusiveMode) + { + // This is to make sure that the callback uses actualBufferSize in case of exclusive mode + if (inputDevice != nullptr && outputDevice != nullptr && inputDevice->actualBufferSize != outputDevice->actualBufferSize) + { + close(); + lastError = TRANS("Couldn't open the output device (buffer size mismatch)"); + return lastError; + } + + currentBufferSizeSamples = outputDevice != nullptr ? outputDevice->actualBufferSize + : inputDevice->actualBufferSize; + } + if (inputDevice != nullptr) ResetEvent (inputDevice->clientEvent); if (outputDevice != nullptr) ResetEvent (outputDevice->clientEvent); @@ -955,7 +1118,7 @@ public: { latencyIn = (int) (inputDevice->latencySamples + currentBufferSizeSamples); - if (! check (inputDevice->client->Start())) + if (! inputDevice->start (currentBufferSizeSamples)) { close(); lastError = TRANS("Couldn't start the input device!"); @@ -967,7 +1130,7 @@ public: { latencyOut = (int) (outputDevice->latencySamples + currentBufferSizeSamples); - if (! check (outputDevice->client->Start())) + if (! outputDevice->start()) { close(); lastError = TRANS("Couldn't start the output device!"); @@ -1056,34 +1219,48 @@ public: const int bufferSize = currentBufferSizeSamples; const int numInputBuffers = getActiveInputChannels().countNumberOfSetBits(); const int numOutputBuffers = getActiveOutputChannels().countNumberOfSetBits(); - bool sampleRateChanged = false; + bool sampleRateHasChanged = false; AudioSampleBuffer ins (jmax (1, numInputBuffers), bufferSize + 32); AudioSampleBuffer outs (jmax (1, numOutputBuffers), bufferSize + 32); float** const inputBuffers = ins.getArrayOfWritePointers(); float** const outputBuffers = outs.getArrayOfWritePointers(); ins.clear(); + outs.clear(); while (! threadShouldExit()) { if (inputDevice != nullptr) { - inputDevice->copyBuffers (inputBuffers, numInputBuffers, bufferSize, *this); + if (outputDevice == nullptr) + { + if (WaitForSingleObject (inputDevice->clientEvent, 1000) == WAIT_TIMEOUT) + break; - if (threadShouldExit()) - break; + inputDevice->handleDeviceBuffer(); + + if (inputDevice->getNumSamplesInReservoir() < bufferSize) + continue; + } + else + { + if (useExclusiveMode && WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0) + inputDevice->handleDeviceBuffer(); + } + + inputDevice->copyBuffersFromReservoir (inputBuffers, numInputBuffers, bufferSize); if (inputDevice->sampleRateHasChanged) { - sampleRateChanged = true; + sampleRateHasChanged = true; sampleRateChangedByOutput = false; } } { - const ScopedLock sl (startStopLock); + const ScopedTryLock sl (startStopLock); - if (isStarted) + if (sl.isLocked() && isStarted) callback->audioDeviceIOCallback (const_cast (inputBuffers), numInputBuffers, outputBuffers, numOutputBuffers, bufferSize); else @@ -1092,16 +1269,18 @@ public: if (outputDevice != nullptr) { - outputDevice->copyBuffers (const_cast (outputBuffers), numOutputBuffers, bufferSize, *this); + // Note that this function is handed the input device so it can check for the event and make sure + // the input reservoir is filled up correctly even when bufferSize > device actualBufferSize + outputDevice->copyBuffers (const_cast (outputBuffers), numOutputBuffers, bufferSize, inputDevice, *this); if (outputDevice->sampleRateHasChanged) { - sampleRateChanged = true; + sampleRateHasChanged = true; sampleRateChangedByOutput = true; } } - if (sampleRateChanged) + if (sampleRateHasChanged) { triggerAsyncUpdate(); break; // Quit the thread... will restart it later! @@ -1208,9 +1387,10 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType, private DeviceChangeDetector { public: - WASAPIAudioIODeviceType() - : AudioIODeviceType ("Windows Audio"), + WASAPIAudioIODeviceType (bool exclusive) + : AudioIODeviceType (exclusive ? "Windows Audio (Exclusive Mode)" : "Windows Audio"), DeviceChangeDetector (L"Windows Audio"), + exclusiveMode (exclusive), hasScanned (false) { } @@ -1267,7 +1447,6 @@ public: { jassert (hasScanned); // need to call scanForDevices() before doing this - const bool useExclusiveMode = false; ScopedPointer device; const int outputIndex = outputDeviceNames.indexOf (outputDeviceName); @@ -1277,9 +1456,10 @@ public: { device = new WASAPIAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName : inputDeviceName, + getTypeName(), outputDeviceIds [outputIndex], inputDeviceIds [inputIndex], - useExclusiveMode); + exclusiveMode); if (! device->initialise()) device = nullptr; @@ -1293,7 +1473,7 @@ public: StringArray inputDeviceNames, inputDeviceIds; private: - bool hasScanned; + bool exclusiveMode, hasScanned; ComSmartPtr enumerator; //============================================================================== @@ -1418,7 +1598,7 @@ private: } //============================================================================== - void systemDeviceChanged() + void systemDeviceChanged() override { StringArray newOutNames, newInNames, newOutIds, newInIds; scan (newOutNames, newInNames, newOutIds, newInIds); @@ -1493,12 +1673,16 @@ struct MMDeviceMasterVolume } //============================================================================== -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI() +AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool exclusiveMode) { - if (SystemStats::getOperatingSystemType() >= SystemStats::WinVista) - return new WasapiClasses::WASAPIAudioIODeviceType(); + #if ! JUCE_WASAPI_EXCLUSIVE + if (exclusiveMode) + return nullptr; + #endif - return nullptr; + return SystemStats::getOperatingSystemType() >= SystemStats::WinVista + ? new WasapiClasses::WASAPIAudioIODeviceType (exclusiveMode) + : nullptr; } //============================================================================== diff --git a/source/modules/juce_audio_formats/codecs/flac/all.h b/source/modules/juce_audio_formats/codecs/flac/all.h index cc01e0511..c7f30328d 100644 --- a/source/modules/juce_audio_formats/codecs/flac/all.h +++ b/source/modules/juce_audio_formats/codecs/flac/all.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/source/modules/juce_audio_formats/codecs/flac/alloc.h b/source/modules/juce_audio_formats/codecs/flac/alloc.h index 83595ac1b..3aab81c27 100644 --- a/source/modules/juce_audio_formats/codecs/flac/alloc.h +++ b/source/modules/juce_audio_formats/codecs/flac/alloc.h @@ -1,6 +1,6 @@ /* alloc - Convenience routines for safely allocating memory * Copyright (C) 2007-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,7 +33,7 @@ #ifndef FLAC__SHARE__ALLOC_H #define FLAC__SHARE__ALLOC_H -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H # include #endif diff --git a/source/modules/juce_audio_formats/codecs/flac/assert.h b/source/modules/juce_audio_formats/codecs/flac/assert.h index fab30f7e8..f02aeac10 100644 --- a/source/modules/juce_audio_formats/codecs/flac/assert.h +++ b/source/modules/juce_audio_formats/codecs/flac/assert.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2001-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/source/modules/juce_audio_formats/codecs/flac/callback.h b/source/modules/juce_audio_formats/codecs/flac/callback.h index 5f58552ce..9928843bb 100644 --- a/source/modules/juce_audio_formats/codecs/flac/callback.h +++ b/source/modules/juce_audio_formats/codecs/flac/callback.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2004-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/source/modules/juce_audio_formats/codecs/flac/compat.h b/source/modules/juce_audio_formats/codecs/flac/compat.h index 6c436cc10..a3dc7c7b9 100644 --- a/source/modules/juce_audio_formats/codecs/flac/compat.h +++ b/source/modules/juce_audio_formats/codecs/flac/compat.h @@ -1,5 +1,5 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2012 Xiph.org Foundation + * Copyright (C) 2012-2014 Xiph.org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -73,23 +73,25 @@ #endif #if defined(_MSC_VER) -#if _MSC_VER < 1500 -/* Visual Studio 2008 has restrict. */ -#define restrict __restrict -#endif #define inline __inline #endif -/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ -#ifdef _MSC_VER -#define FLAC__U64L(x) x +#if defined __INTEL_COMPILER || (defined _MSC_VER && defined _WIN64) +/* MSVS generates VERY slow 32-bit code with __restrict */ +#define flac_restrict __restrict +#elif defined __GNUC__ +#define flac_restrict __restrict__ #else -#define FLAC__U64L(x) x##LLU +#define flac_restrict #endif +#define FLAC__U64L(x) x##ULL + #if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ +#define FLAC__STRCASECMP stricmp #define FLAC__STRNCASECMP strnicmp #else +#define FLAC__STRCASECMP strcasecmp #define FLAC__STRNCASECMP strncasecmp #endif @@ -139,6 +141,7 @@ #ifdef _WIN32 /* All char* strings are in UTF-8 format. Added to support Unicode files on Windows */ +#include "win_utf8_io.h" #define flac_printf printf_utf8 #define flac_fprintf fprintf_utf8 @@ -160,12 +163,7 @@ #define flac_utime utime #define flac_unlink unlink #define flac_rename rename - -#ifdef _WIN32 -#define flac_stat _stat64 -#else #define flac_stat stat -#endif #endif @@ -177,8 +175,14 @@ #define flac_fstat fstat #endif +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 +#endif +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif -/* FLAC needs to compile and work correctly on systems with a norrmal ISO C99 +/* FLAC needs to compile and work correctly on systems with a normal ISO C99 * snprintf as well as Microsoft Visual Studio which has an non-standards * conformant snprint_s function. * @@ -188,6 +192,7 @@ extern "C" { #endif int flac_snprintf(char *str, size_t size, const char *fmt, ...); +int flac_vsnprintf(char *str, size_t size, const char *fmt, va_list va); #ifdef __cplusplus }; #endif diff --git a/source/modules/juce_audio_formats/codecs/flac/endswap.h b/source/modules/juce_audio_formats/codecs/flac/endswap.h index 2e09404bf..b2a7e8550 100644 --- a/source/modules/juce_audio_formats/codecs/flac/endswap.h +++ b/source/modules/juce_audio_formats/codecs/flac/endswap.h @@ -1,5 +1,5 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2012 Xiph.org Foundation + * Copyright (C) 2012-2014 Xiph.org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,20 +33,46 @@ #if HAVE_BSWAP32 /* GCC and Clang */ +/* GCC prior to 4.8 didn't provide bswap16 on x86_64 */ +#if ! HAVE_BSWAP16 +static inline unsigned short __builtin_bswap16(unsigned short a) +{ + return (a<<8)|(a>>8); +} +#endif + +#define ENDSWAP_16(x) (__builtin_bswap16 (x)) #define ENDSWAP_32(x) (__builtin_bswap32 (x)) #elif defined _MSC_VER /* Windows. Apparently in . */ +#define ENDSWAP_16(x) (_byteswap_ushort (x)) #define ENDSWAP_32(x) (_byteswap_ulong (x)) #elif defined HAVE_BYTESWAP_H /* Linux */ #include +#define ENDSWAP_16(x) (bswap_16 (x)) #define ENDSWAP_32(x) (bswap_32 (x)) #else -#define ENDSWAP_32(x) ((((x) >> 24) & 0xFF) + (((x) >> 8) & 0xFF00) + (((x) & 0xFF00) << 8) + (((x) & 0xFF) << 24)) +#define ENDSWAP_16(x) ((((x) >> 8) & 0xFF) | (((x) & 0xFF) << 8)) +#define ENDSWAP_32(x) ((((x) >> 24) & 0xFF) | (((x) >> 8) & 0xFF00) | (((x) & 0xFF00) << 8) | (((x) & 0xFF) << 24)) + +#endif + + +/* Host to little-endian byte swapping. */ +#if CPU_IS_BIG_ENDIAN + +#define H2LE_16(x) ENDSWAP_16 (x) +#define H2LE_32(x) ENDSWAP_32 (x) + +#else + +#define H2LE_16(x) (x) +#define H2LE_32(x) (x) #endif diff --git a/source/modules/juce_audio_formats/codecs/flac/export.h b/source/modules/juce_audio_formats/codecs/flac/export.h index d7ca735ee..30f018be9 100644 --- a/source/modules/juce_audio_formats/codecs/flac/export.h +++ b/source/modules/juce_audio_formats/codecs/flac/export.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -61,9 +61,9 @@ #elif defined(_MSC_VER) #ifdef FLAC_API_EXPORTS -#define FLAC_API _declspec(dllexport) +#define FLAC_API __declspec(dllexport) #else -#define FLAC_API _declspec(dllimport) +#define FLAC_API __declspec(dllimport) #endif #elif defined(FLAC__USE_VISIBILITY_ATTR) diff --git a/source/modules/juce_audio_formats/codecs/flac/format.h b/source/modules/juce_audio_formats/codecs/flac/format.h index ecebe8028..a151b6735 100644 --- a/source/modules/juce_audio_formats/codecs/flac/format.h +++ b/source/modules/juce_audio_formats/codecs/flac/format.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -509,9 +509,11 @@ typedef enum { FLAC__METADATA_TYPE_PICTURE = 6, /**< PICTURE block */ - FLAC__METADATA_TYPE_UNDEFINED = 7 + FLAC__METADATA_TYPE_UNDEFINED = 7, /**< marker to denote beginning of undefined type range; this number will increase as new metadata types are added */ + FLAC__MAX_METADATA_TYPE = FLAC__MAX_METADATA_TYPE_CODE, + /**< No type will ever be greater than this. There is not enough room in the protocol block. */ } FLAC__MetadataType; /** Maps a FLAC__MetadataType to a C string. diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/bitmath.c b/source/modules/juce_audio_formats/codecs/flac/libFLAC/bitmath.c index 3ea040efe..e011f8416 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/bitmath.c +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/bitmath.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2001-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,12 +30,11 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H # include #endif #include "include/private/bitmath.h" -#include "../assert.h" /* An example of what FLAC__bitmath_silog2() computes: * diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/bitreader.c b/source/modules/juce_audio_formats/codecs/flac/libFLAC/bitreader.c index 3fc7e1df6..a4632e37b 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/bitreader.c +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/bitreader.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,7 +30,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H # include #endif @@ -73,7 +73,6 @@ */ static const unsigned FLAC__BITREADER_DEFAULT_CAPACITY = 65536u / FLAC__BITS_PER_WORD; /* in words */ -/* WATCHOUT: assembly routines rely on the order in which these fields are declared */ struct FLAC__BitReader { /* any partially-consumed word at the head will stay right-justified as bits are consumed from the left */ /* any incomplete word at the tail will be left-justified, and bytes from the read callback are added on the right */ @@ -87,7 +86,6 @@ struct FLAC__BitReader { unsigned crc16_align; /* the number of bits in the current consumed word that should not be CRC'd */ FLAC__BitReaderReadCallback read_callback; void *client_data; - FLAC__CPUInfo cpu_info; }; static inline void crc16_update_word_(FLAC__BitReader *br, uint32_t word) @@ -119,8 +117,7 @@ static inline void crc16_update_word_(FLAC__BitReader *br, uint32_t word) br->crc16_align = 0; } -/* would be static except it needs to be called by asm routines */ -FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br) +static FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br) { unsigned start, end; size_t bytes; @@ -231,7 +228,7 @@ void FLAC__bitreader_delete(FLAC__BitReader *br) * ***********************************************************************/ -FLAC__bool FLAC__bitreader_init(FLAC__BitReader *br, FLAC__CPUInfo cpu, FLAC__BitReaderReadCallback rcb, void *cd) +FLAC__bool FLAC__bitreader_init(FLAC__BitReader *br, FLAC__BitReaderReadCallback rcb, void *cd) { FLAC__ASSERT(0 != br); @@ -243,7 +240,6 @@ FLAC__bool FLAC__bitreader_init(FLAC__BitReader *br, FLAC__CPUInfo cpu, FLAC__Bi return false; br->read_callback = rcb; br->client_data = cd; - br->cpu_info = cpu; return true; } @@ -1048,9 +1044,9 @@ FLAC__bool FLAC__bitreader_read_utf8_uint64(FLAC__BitReader *br, FLAC__uint64 *v return true; } -/* These functions a declared inline in this file but are also callable as +/* These functions are declared inline in this file but are also callable as * externs from elsewhere. - * According to the C99 sepc, section 6.7.4, simply providing a function + * According to the C99 spec, section 6.7.4, simply providing a function * prototype in a header file without 'inline' and making the function inline * in this file should be sufficient. * Unfortunately, the Microsoft VS compiler doesn't pick them up externally. To diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/bitwriter.c b/source/modules/juce_audio_formats/codecs/flac/libFLAC/bitwriter.c index 9ac9ee539..565fbcd36 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/bitwriter.c +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/bitwriter.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,7 +30,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H # include #endif @@ -47,8 +47,7 @@ /* WATCHOUT: if you change this you must also change the following #defines down to SWAP_BE_WORD_TO_HOST below to match */ /* WATCHOUT: there are a few places where the code will not work unless uint32_t is >= 32 bits wide */ #define FLAC__BYTES_PER_WORD 4 -#undef FLAC__BITS_PER_WORD -#define FLAC__BITS_PER_WORD 32 +#define FLAC__BITS_PER_WORD (8 * FLAC__BYTES_PER_WORD) #define FLAC__WORD_ALL_ONES ((FLAC__uint32)0xffffffff) /* SWAP_BE_WORD_TO_HOST swaps bytes in a uint32_t (which is always big-endian) if necessary to match host byte order */ #if WORDS_BIGENDIAN @@ -524,28 +523,6 @@ FLAC__bool FLAC__bitwriter_write_rice_signed_block(FLAC__BitWriter *bw, const FL msbits = uval >> parameter; -#if 0 /* OPT: can remove this special case if it doesn't make up for the extra compare (doesn't make a statistically significant difference with msvc or gcc/x86) */ - if(bw->bits && bw->bits + msbits + lsbits <= FLAC__BITS_PER_WORD) { /* i.e. if the whole thing fits in the current uint32_t */ - /* ^^^ if bw->bits is 0 then we may have filled the buffer and have no free uint32_t to work in */ - bw->bits = bw->bits + msbits + lsbits; - uval |= mask1; /* set stop bit */ - uval &= mask2; /* mask off unused top bits */ - /* NOT: bw->accum <<= msbits + lsbits because msbits+lsbits could be 32, then the shift would be a NOP */ - bw->accum <<= msbits; - bw->accum <<= lsbits; - bw->accum |= uval; - if(bw->bits == FLAC__BITS_PER_WORD) { - bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); - bw->bits = 0; - /* burying the capacity check down here means we have to grow the buffer a little if there are more vals to do */ - if(bw->capacity <= bw->words && nvals > 1 && !bitwriter_grow_(bw, 1)) { - FLAC__ASSERT(bw->capacity == bw->words); - return false; - } - } - } - else { -#elif 1 /*@@@@@@ OPT: try this version with MSVC6 to see if better, not much difference for gcc-4 */ if(bw->bits && bw->bits + msbits + lsbits < FLAC__BITS_PER_WORD) { /* i.e. if the whole thing fits in the current uint32_t */ /* ^^^ if bw->bits is 0 then we may have filled the buffer and have no free uint32_t to work in */ bw->bits = bw->bits + msbits + lsbits; @@ -555,7 +532,6 @@ FLAC__bool FLAC__bitwriter_write_rice_signed_block(FLAC__BitWriter *bw, const FL bw->accum |= uval; } else { -#endif /* slightly pessimistic size check but faster than "<= bw->words + (bw->bits+msbits+lsbits+FLAC__BITS_PER_WORD-1)/FLAC__BITS_PER_WORD" */ /* OPT: pessimism may cause flurry of false calls to grow_ which eat up all savings before it */ if(bw->capacity <= bw->words + bw->bits + msbits + 1/*lsbits always fit in 1 uint32_t*/ && !bitwriter_grow_(bw, msbits+lsbits)) @@ -610,9 +586,7 @@ break1: bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); bw->accum = uval; } -#if 1 } -#endif vals++; nvals--; } @@ -853,9 +827,9 @@ FLAC__bool FLAC__bitwriter_zero_pad_to_byte_boundary(FLAC__BitWriter *bw) return true; } -/* These functions a declared inline in this file but are also callable as +/* These functions are declared inline in this file but are also callable as * externs from elsewhere. - * According to the C99 sepc, section 6.7.4, simply providing a function + * According to the C99 spec, section 6.7.4, simply providing a function * prototype in a header file without 'inline' and making the function inline * in this file should be sufficient. * Unfortunately, the Microsoft VS compiler doesn't pick them up externally. To diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/cpu.c b/source/modules/juce_audio_formats/codecs/flac/libFLAC/cpu.c index e352c9535..46db9e3a5 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/cpu.c +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/cpu.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2001-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,47 +30,47 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H # include #endif #include "include/private/cpu.h" -#include -#include + +#if 0 + #include + #include + #include +#endif #if defined FLAC__CPU_IA32 # include -#elif defined FLAC__CPU_PPC -# if !defined FLAC__NO_ASM -# if defined FLAC__SYS_DARWIN -# include -# include -# include -# include -# include -# ifndef CPU_SUBTYPE_POWERPC_970 -# define CPU_SUBTYPE_POWERPC_970 ((cpu_subtype_t) 100) -# endif -# else /* FLAC__SYS_DARWIN */ - -# include -# include - -static sigjmp_buf jmpbuf; -static volatile sig_atomic_t canjump = 0; - -static void sigill_handler (int sig) + +static void disable_sse(FLAC__CPUInfo *info) { - if (!canjump) { - signal (sig, SIG_DFL); - raise (sig); - } - canjump = 0; - siglongjmp (jmpbuf, 1); + info->ia32.sse = false; + info->ia32.sse2 = false; + info->ia32.sse3 = false; + info->ia32.ssse3 = false; + info->ia32.sse41 = false; + info->ia32.sse42 = false; +} + +static void disable_avx(FLAC__CPUInfo *info) +{ + info->ia32.avx = false; + info->ia32.avx2 = false; + info->ia32.fma = false; } -# endif /* FLAC__SYS_DARWIN */ -# endif /* FLAC__NO_ASM */ -#endif /* FLAC__CPU_PPC */ + +#elif defined FLAC__CPU_X86_64 + +static void disable_avx(FLAC__CPUInfo *info) +{ + info->x86.avx = false; + info->x86.avx2 = false; + info->x86.fma = false; +} +#endif #if defined (__NetBSD__) || defined(__OpenBSD__) #include @@ -87,25 +87,34 @@ static void sigill_handler (int sig) /* how to get sysctlbyname()? */ #endif +#ifdef FLAC__CPU_IA32 /* these are flags in EDX of CPUID AX=00000001 */ static const unsigned FLAC__CPUINFO_IA32_CPUID_CMOV = 0x00008000; static const unsigned FLAC__CPUINFO_IA32_CPUID_MMX = 0x00800000; static const unsigned FLAC__CPUINFO_IA32_CPUID_FXSR = 0x01000000; static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE = 0x02000000; static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE2 = 0x04000000; +#endif + /* these are flags in ECX of CPUID AX=00000001 */ static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE3 = 0x00000001; static const unsigned FLAC__CPUINFO_IA32_CPUID_SSSE3 = 0x00000200; -/* these are flags in EDX of CPUID AX=80000001 */ -static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_3DNOW = 0x80000000; -static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXT3DNOW = 0x40000000; -static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXTMMX = 0x00400000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE41 = 0x00080000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE42 = 0x00100000; +#if defined FLAC__AVX_SUPPORTED +/* these are flags in ECX of CPUID AX=00000001 */ +static const unsigned FLAC__CPUINFO_IA32_CPUID_OSXSAVE = 0x08000000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_AVX = 0x10000000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_FMA = 0x00001000; +/* these are flags in EBX of CPUID AX=00000007 */ +static const unsigned FLAC__CPUINFO_IA32_CPUID_AVX2 = 0x00000020; +#endif /* * Extra stuff needed for detection of OS support for SSE on IA-32 */ -#if defined(FLAC__CPU_IA32) && !defined FLAC__NO_ASM && defined FLAC__HAS_NASM && !defined FLAC__NO_SSE_OS && !defined FLAC__SSE_OS +#if defined(FLAC__CPU_IA32) && !defined FLAC__NO_ASM && (defined FLAC__HAS_NASM || defined FLAC__HAS_X86INTRIN) && !defined FLAC__NO_SSE_OS && !defined FLAC__SSE_OS # if defined(__linux__) /* * If the OS doesn't support SSE, we will get here with a SIGILL. We @@ -120,35 +129,14 @@ static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXTMMX = 0x00400000; * 6 bytes extra in case our estimate is wrong * 12 bytes puts us in the NOP "landing zone" */ -# undef USE_OBSOLETE_SIGCONTEXT_FLAVOR /* #define this to use the older signal handler method */ -# ifdef USE_OBSOLETE_SIGCONTEXT_FLAVOR - static void sigill_handler_sse_os(int signal, struct sigcontext sc) - { - (void)signal; - sc.eip += 3 + 3 + 6; - } -# else # include static void sigill_handler_sse_os(int signal, siginfo_t *si, void *uc) { (void)signal, (void)si; ((ucontext_t*)uc)->uc_mcontext.gregs[14/*REG_EIP*/] += 3 + 3 + 6; } -# endif # elif defined(_MSC_VER) # include -# define USE_TRY_CATCH_FLAVOR /* sigill_handler flavor resulted in several crash reports on win32 */ -# ifdef USE_TRY_CATCH_FLAVOR -# else - LONG CALLBACK sigill_handler_sse_os(EXCEPTION_POINTERS *ep) - { - if(ep->ExceptionRecord->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION) { - ep->ContextRecord->Eip += 3 + 3 + 6; - return EXCEPTION_CONTINUE_EXECUTION; - } - return EXCEPTION_CONTINUE_SEARCH; - } -# endif # endif #endif @@ -159,261 +147,344 @@ void FLAC__cpu_info(FLAC__CPUInfo *info) * IA32-specific */ #ifdef FLAC__CPU_IA32 + FLAC__bool ia32_fxsr = false; + FLAC__bool ia32_osxsave = false; + (void) ia32_fxsr; (void) ia32_osxsave; /* to avoid warnings about unused variables */ + memset(info, 0, sizeof(*info)); info->type = FLAC__CPUINFO_TYPE_IA32; -#if !defined FLAC__NO_ASM && defined FLAC__HAS_NASM +#if !defined FLAC__NO_ASM && (defined FLAC__HAS_NASM || defined FLAC__HAS_X86INTRIN) info->use_asm = true; /* we assume a minimum of 80386 with FLAC__CPU_IA32 */ - info->data.ia32.cpuid = FLAC__cpu_have_cpuid_asm_ia32()? true : false; - info->data.ia32.bswap = info->data.ia32.cpuid; /* CPUID => BSWAP since it came after */ - info->data.ia32.cmov = false; - info->data.ia32.mmx = false; - info->data.ia32.fxsr = false; - info->data.ia32.sse = false; - info->data.ia32.sse2 = false; - info->data.ia32.sse3 = false; - info->data.ia32.ssse3 = false; - info->data.ia32._3dnow = false; - info->data.ia32.ext3dnow = false; - info->data.ia32.extmmx = false; - if(info->data.ia32.cpuid) { - /* http://www.sandpile.org/ia32/cpuid.htm */ - FLAC__uint32 flags_edx, flags_ecx; - FLAC__cpu_info_asm_ia32(&flags_edx, &flags_ecx); - info->data.ia32.cmov = (flags_edx & FLAC__CPUINFO_IA32_CPUID_CMOV )? true : false; - info->data.ia32.mmx = (flags_edx & FLAC__CPUINFO_IA32_CPUID_MMX )? true : false; - info->data.ia32.fxsr = (flags_edx & FLAC__CPUINFO_IA32_CPUID_FXSR )? true : false; - info->data.ia32.sse = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE )? true : false; - info->data.ia32.sse2 = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE2 )? true : false; - info->data.ia32.sse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE3 )? true : false; - info->data.ia32.ssse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSSE3)? true : false; - -#ifdef FLAC__USE_3DNOW - flags_edx = FLAC__cpu_info_extended_amd_asm_ia32(); - info->data.ia32._3dnow = (flags_edx & FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_3DNOW )? true : false; - info->data.ia32.ext3dnow = (flags_edx & FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXT3DNOW)? true : false; - info->data.ia32.extmmx = (flags_edx & FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXTMMX )? true : false; +#ifdef FLAC__HAS_X86INTRIN + if(!FLAC__cpu_have_cpuid_x86()) + return; +#else + if(!FLAC__cpu_have_cpuid_asm_ia32()) + return; +#endif + { + /* http://www.sandpile.org/x86/cpuid.htm */ +#ifdef FLAC__HAS_X86INTRIN + FLAC__uint32 flags_eax, flags_ebx, flags_ecx, flags_edx; + FLAC__cpu_info_x86(1, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx); #else - info->data.ia32._3dnow = info->data.ia32.ext3dnow = info->data.ia32.extmmx = false; + FLAC__uint32 flags_ecx, flags_edx; + FLAC__cpu_info_asm_ia32(&flags_edx, &flags_ecx); #endif + info->ia32.cmov = (flags_edx & FLAC__CPUINFO_IA32_CPUID_CMOV )? true : false; + info->ia32.mmx = (flags_edx & FLAC__CPUINFO_IA32_CPUID_MMX )? true : false; + ia32_fxsr = (flags_edx & FLAC__CPUINFO_IA32_CPUID_FXSR )? true : false; + info->ia32.sse = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE )? true : false; + info->ia32.sse2 = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE2 )? true : false; + info->ia32.sse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE3 )? true : false; + info->ia32.ssse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSSE3)? true : false; + info->ia32.sse41 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE41)? true : false; + info->ia32.sse42 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE42)? true : false; +#if defined FLAC__HAS_X86INTRIN && defined FLAC__AVX_SUPPORTED + ia32_osxsave = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_OSXSAVE)? true : false; + info->ia32.avx = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_AVX )? true : false; + info->ia32.fma = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_FMA )? true : false; + FLAC__cpu_info_x86(7, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx); + info->ia32.avx2 = (flags_ebx & FLAC__CPUINFO_IA32_CPUID_AVX2 )? true : false; +#endif + } #ifdef DEBUG - fprintf(stderr, "CPU info (IA-32):\n"); - fprintf(stderr, " CPUID ...... %c\n", info->data.ia32.cpuid ? 'Y' : 'n'); - fprintf(stderr, " BSWAP ...... %c\n", info->data.ia32.bswap ? 'Y' : 'n'); - fprintf(stderr, " CMOV ....... %c\n", info->data.ia32.cmov ? 'Y' : 'n'); - fprintf(stderr, " MMX ........ %c\n", info->data.ia32.mmx ? 'Y' : 'n'); - fprintf(stderr, " FXSR ....... %c\n", info->data.ia32.fxsr ? 'Y' : 'n'); - fprintf(stderr, " SSE ........ %c\n", info->data.ia32.sse ? 'Y' : 'n'); - fprintf(stderr, " SSE2 ....... %c\n", info->data.ia32.sse2 ? 'Y' : 'n'); - fprintf(stderr, " SSE3 ....... %c\n", info->data.ia32.sse3 ? 'Y' : 'n'); - fprintf(stderr, " SSSE3 ...... %c\n", info->data.ia32.ssse3 ? 'Y' : 'n'); - fprintf(stderr, " 3DNow! ..... %c\n", info->data.ia32._3dnow ? 'Y' : 'n'); - fprintf(stderr, " 3DNow!-ext . %c\n", info->data.ia32.ext3dnow? 'Y' : 'n'); - fprintf(stderr, " 3DNow!-MMX . %c\n", info->data.ia32.extmmx ? 'Y' : 'n'); + fprintf(stderr, "CPU info (IA-32):\n"); + fprintf(stderr, " CMOV ....... %c\n", info->ia32.cmov ? 'Y' : 'n'); + fprintf(stderr, " MMX ........ %c\n", info->ia32.mmx ? 'Y' : 'n'); + fprintf(stderr, " SSE ........ %c\n", info->ia32.sse ? 'Y' : 'n'); + fprintf(stderr, " SSE2 ....... %c\n", info->ia32.sse2 ? 'Y' : 'n'); + fprintf(stderr, " SSE3 ....... %c\n", info->ia32.sse3 ? 'Y' : 'n'); + fprintf(stderr, " SSSE3 ...... %c\n", info->ia32.ssse3 ? 'Y' : 'n'); + fprintf(stderr, " SSE41 ...... %c\n", info->ia32.sse41 ? 'Y' : 'n'); + fprintf(stderr, " SSE42 ...... %c\n", info->ia32.sse42 ? 'Y' : 'n'); +# if defined FLAC__HAS_X86INTRIN && defined FLAC__AVX_SUPPORTED + fprintf(stderr, " AVX ........ %c\n", info->ia32.avx ? 'Y' : 'n'); + fprintf(stderr, " FMA ........ %c\n", info->ia32.fma ? 'Y' : 'n'); + fprintf(stderr, " AVX2 ....... %c\n", info->ia32.avx2 ? 'Y' : 'n'); +# endif #endif - /* - * now have to check for OS support of SSE/SSE2 - */ - if(info->data.ia32.fxsr || info->data.ia32.sse || info->data.ia32.sse2) { + /* + * now have to check for OS support of SSE instructions + */ + if(info->ia32.sse) { #if defined FLAC__NO_SSE_OS - /* assume user knows better than us; turn it off */ - info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; + /* assume user knows better than us; turn it off */ + disable_sse(info); #elif defined FLAC__SSE_OS - /* assume user knows better than us; leave as detected above */ + /* assume user knows better than us; leave as detected above */ #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) || defined(__APPLE__) - int sse = 0; - size_t len; - /* at least one of these must work: */ - len = sizeof(sse); sse = sse || (sysctlbyname("hw.instruction_sse", &sse, &len, NULL, 0) == 0 && sse); - len = sizeof(sse); sse = sse || (sysctlbyname("hw.optional.sse" , &sse, &len, NULL, 0) == 0 && sse); /* __APPLE__ ? */ - if(!sse) - info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; + int sse = 0; + size_t len; + /* at least one of these must work: */ + len = sizeof(sse); sse = sse || (sysctlbyname("hw.instruction_sse", &sse, &len, NULL, 0) == 0 && sse); + len = sizeof(sse); sse = sse || (sysctlbyname("hw.optional.sse" , &sse, &len, NULL, 0) == 0 && sse); /* __APPLE__ ? */ + if(!sse) + disable_sse(info); #elif defined(__NetBSD__) || defined (__OpenBSD__) # if __NetBSD_Version__ >= 105250000 || (defined __OpenBSD__) - int val = 0, mib[2] = { CTL_MACHDEP, CPU_SSE }; - size_t len = sizeof(val); - if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val) - info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; - else { /* double-check SSE2 */ - mib[1] = CPU_SSE2; - len = sizeof(val); - if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val) - info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; + int val = 0, mib[2] = { CTL_MACHDEP, CPU_SSE }; + size_t len = sizeof(val); + if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val) + disable_sse(info); + else { /* double-check SSE2 */ + mib[1] = CPU_SSE2; + len = sizeof(val); + if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val) { + disable_sse(info); + info->ia32.sse = true; } + } # else - info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; + disable_sse(info); # endif #elif defined(__linux__) - int sse = 0; - struct sigaction sigill_save; -#ifdef USE_OBSOLETE_SIGCONTEXT_FLAVOR - if(0 == sigaction(SIGILL, NULL, &sigill_save) && signal(SIGILL, (void (*)(int))sigill_handler_sse_os) != SIG_ERR) -#else - struct sigaction sigill_sse; - sigill_sse.sa_sigaction = sigill_handler_sse_os; - __sigemptyset(&sigill_sse.sa_mask); - sigill_sse.sa_flags = SA_SIGINFO | SA_RESETHAND; /* SA_RESETHAND just in case our SIGILL return jump breaks, so we don't get stuck in a loop */ - if(0 == sigaction(SIGILL, &sigill_sse, &sigill_save)) -#endif - { - /* http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html */ - /* see sigill_handler_sse_os() for an explanation of the following: */ - asm volatile ( - "xorl %0,%0\n\t" /* for some reason, still need to do this to clear 'sse' var */ - "xorps %%xmm0,%%xmm0\n\t" /* will cause SIGILL if unsupported by OS */ - "incl %0\n\t" /* SIGILL handler will jump over this */ - /* landing zone */ - "nop\n\t" /* SIGILL jump lands here if "inc" is 9 bytes */ - "nop\n\t" - "nop\n\t" - "nop\n\t" - "nop\n\t" - "nop\n\t" - "nop\n\t" /* SIGILL jump lands here if "inc" is 3 bytes (expected) */ - "nop\n\t" - "nop" /* SIGILL jump lands here if "inc" is 1 byte */ - : "=r"(sse) - : "r"(sse) - ); - - sigaction(SIGILL, &sigill_save, NULL); - } + int sse = 0; + struct sigaction sigill_save; + struct sigaction sigill_sse; + sigill_sse.sa_sigaction = sigill_handler_sse_os; + __sigemptyset(&sigill_sse.sa_mask); + sigill_sse.sa_flags = SA_SIGINFO | SA_RESETHAND; /* SA_RESETHAND just in case our SIGILL return jump breaks, so we don't get stuck in a loop */ + if(0 == sigaction(SIGILL, &sigill_sse, &sigill_save)) + { + /* http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html */ + /* see sigill_handler_sse_os() for an explanation of the following: */ + asm volatile ( + "xorps %%xmm0,%%xmm0\n\t" /* will cause SIGILL if unsupported by OS */ + "incl %0\n\t" /* SIGILL handler will jump over this */ + /* landing zone */ + "nop\n\t" /* SIGILL jump lands here if "inc" is 9 bytes */ + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" /* SIGILL jump lands here if "inc" is 3 bytes (expected) */ + "nop\n\t" + "nop" /* SIGILL jump lands here if "inc" is 1 byte */ + : "=r"(sse) + : "0"(sse) + ); - if(!sse) - info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; + sigaction(SIGILL, &sigill_save, NULL); + } + + if(!sse) + disable_sse(info); #elif defined(_MSC_VER) -# ifdef USE_TRY_CATCH_FLAVOR - _try { - __asm { -# if _MSC_VER <= 1200 - /* VC6 assembler doesn't know SSE, have to emit bytecode instead */ - _emit 0x0F - _emit 0x57 - _emit 0xC0 -# else - xorps xmm0,xmm0 -# endif - } - } - _except(EXCEPTION_EXECUTE_HANDLER) { - if (_exception_code() == STATUS_ILLEGAL_INSTRUCTION) - info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; - } -# else - int sse = 0; - LPTOP_LEVEL_EXCEPTION_FILTER save = SetUnhandledExceptionFilter(sigill_handler_sse_os); - /* see GCC version above for explanation */ - /* http://msdn2.microsoft.com/en-us/library/4ks26t93.aspx */ - /* http://www.codeproject.com/cpp/gccasm.asp */ - /* http://www.hick.org/~mmiller/msvc_inline_asm.html */ + __try { __asm { -# if _MSC_VER <= 1200 - /* VC6 assembler doesn't know SSE, have to emit bytecode instead */ - _emit 0x0F - _emit 0x57 - _emit 0xC0 -# else xorps xmm0,xmm0 -# endif - inc sse - nop - nop - nop - nop - nop - nop - nop - nop - nop } - SetUnhandledExceptionFilter(save); - if(!sse) - info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; -# endif + } + __except(EXCEPTION_EXECUTE_HANDLER) { + if (_exception_code() == STATUS_ILLEGAL_INSTRUCTION) + disable_sse(info); + } +#elif defined(__GNUC__) /* MinGW goes here */ + int sse = 0; + /* Based on the idea described in Agner Fog's manual "Optimizing subroutines in assembly language" */ + /* In theory, not guaranteed to detect lack of OS SSE support on some future Intel CPUs, but in practice works (see the aforementioned manual) */ + if (ia32_fxsr) { + struct { + FLAC__uint32 buff[128]; + } __attribute__((aligned(16))) fxsr; + FLAC__uint32 old_val, new_val; + + asm volatile ("fxsave %0" : "=m" (fxsr) : "m" (fxsr)); + old_val = fxsr.buff[50]; + fxsr.buff[50] ^= 0x0013c0de; /* change value in the buffer */ + asm volatile ("fxrstor %0" : "=m" (fxsr) : "m" (fxsr)); /* try to change SSE register */ + fxsr.buff[50] = old_val; /* restore old value in the buffer */ + asm volatile ("fxsave %0 " : "=m" (fxsr) : "m" (fxsr)); /* old value will be overwritten if SSE register was changed */ + new_val = fxsr.buff[50]; /* == old_val if FXRSTOR didn't change SSE register and (old_val ^ 0x0013c0de) otherwise */ + fxsr.buff[50] = old_val; /* again restore old value in the buffer */ + asm volatile ("fxrstor %0" : "=m" (fxsr) : "m" (fxsr)); /* restore old values of registers */ + + if ((old_val^new_val) == 0x0013c0de) + sse = 1; + } + if(!sse) + disable_sse(info); #else - /* no way to test, disable to be safe */ - info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; + /* no way to test, disable to be safe */ + disable_sse(info); #endif #ifdef DEBUG - fprintf(stderr, " SSE OS sup . %c\n", info->data.ia32.sse ? 'Y' : 'n'); + fprintf(stderr, " SSE OS sup . %c\n", info->ia32.sse ? 'Y' : 'n'); #endif + } + else /* info->ia32.sse == false */ + disable_sse(info); - } + /* + * now have to check for OS support of AVX instructions + */ + if(info->ia32.avx && ia32_osxsave) { + FLAC__uint32 ecr = FLAC__cpu_xgetbv_x86(); + if ((ecr & 0x6) != 0x6) + disable_avx(info); +#ifdef DEBUG + fprintf(stderr, " AVX OS sup . %c\n", info->ia32.avx ? 'Y' : 'n'); +#endif } + else /* no OS AVX support*/ + disable_avx(info); #else info->use_asm = false; #endif /* - * PPC-specific + * x86-64-specific */ -#elif defined FLAC__CPU_PPC - info->type = FLAC__CPUINFO_TYPE_PPC; -# if !defined FLAC__NO_ASM +#elif defined FLAC__CPU_X86_64 + FLAC__bool x86_osxsave = false; + (void) x86_osxsave; /* to avoid warnings about unused variables */ + memset(info, 0, sizeof(*info)); + info->type = FLAC__CPUINFO_TYPE_X86_64; +#if !defined FLAC__NO_ASM && defined FLAC__HAS_X86INTRIN info->use_asm = true; -# ifdef FLAC__USE_ALTIVEC -# if defined FLAC__SYS_DARWIN { - int val = 0, mib[2] = { CTL_HW, HW_VECTORUNIT }; - size_t len = sizeof(val); - info->data.ppc.altivec = !(sysctl(mib, 2, &val, &len, NULL, 0) || !val); + /* http://www.sandpile.org/x86/cpuid.htm */ + FLAC__uint32 flags_eax, flags_ebx, flags_ecx, flags_edx; + FLAC__cpu_info_x86(1, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx); + info->x86.sse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE3 )? true : false; + info->x86.ssse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSSE3)? true : false; + info->x86.sse41 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE41)? true : false; + info->x86.sse42 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE42)? true : false; +#if defined FLAC__AVX_SUPPORTED + x86_osxsave = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_OSXSAVE)? true : false; + info->x86.avx = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_AVX )? true : false; + info->x86.fma = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_FMA )? true : false; + FLAC__cpu_info_x86(7, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx); + info->x86.avx2 = (flags_ebx & FLAC__CPUINFO_IA32_CPUID_AVX2 )? true : false; +#endif } - { - host_basic_info_data_t hostInfo; - mach_msg_type_number_t infoCount; - - infoCount = HOST_BASIC_INFO_COUNT; - host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)&hostInfo, &infoCount); +#ifdef DEBUG + fprintf(stderr, "CPU info (x86-64):\n"); + fprintf(stderr, " SSE3 ....... %c\n", info->x86.sse3 ? 'Y' : 'n'); + fprintf(stderr, " SSSE3 ...... %c\n", info->x86.ssse3 ? 'Y' : 'n'); + fprintf(stderr, " SSE41 ...... %c\n", info->x86.sse41 ? 'Y' : 'n'); + fprintf(stderr, " SSE42 ...... %c\n", info->x86.sse42 ? 'Y' : 'n'); +# if defined FLAC__AVX_SUPPORTED + fprintf(stderr, " AVX ........ %c\n", info->x86.avx ? 'Y' : 'n'); + fprintf(stderr, " FMA ........ %c\n", info->x86.fma ? 'Y' : 'n'); + fprintf(stderr, " AVX2 ....... %c\n", info->x86.avx2 ? 'Y' : 'n'); +# endif +#endif - info->data.ppc.ppc64 = (hostInfo.cpu_type == CPU_TYPE_POWERPC) && (hostInfo.cpu_subtype == CPU_SUBTYPE_POWERPC_970); + /* + * now have to check for OS support of AVX instructions + */ + if(info->x86.avx && x86_osxsave) { + FLAC__uint32 ecr = FLAC__cpu_xgetbv_x86(); + if ((ecr & 0x6) != 0x6) + disable_avx(info); +#ifdef DEBUG + fprintf(stderr, " AVX OS sup . %c\n", info->x86.avx ? 'Y' : 'n'); +#endif } -# else /* FLAC__USE_ALTIVEC && !FLAC__SYS_DARWIN */ - { - /* no Darwin, do it the brute-force way */ - /* @@@@@@ this is not thread-safe; replace with SSE OS method above or remove */ - info->data.ppc.altivec = 0; - info->data.ppc.ppc64 = 0; + else /* no OS AVX support*/ + disable_avx(info); +#else + info->use_asm = false; +#endif - signal (SIGILL, sigill_handler); - canjump = 0; - if (!sigsetjmp (jmpbuf, 1)) { - canjump = 1; +/* + * unknown CPU + */ +#else + info->type = FLAC__CPUINFO_TYPE_UNKNOWN; + info->use_asm = false; +#endif +} - asm volatile ( - "mtspr 256, %0\n\t" - "vand %%v0, %%v0, %%v0" - : - : "r" (-1) - ); +#if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && defined FLAC__HAS_X86INTRIN - info->data.ppc.altivec = 1; - } - canjump = 0; - if (!sigsetjmp (jmpbuf, 1)) { - int x = 0; - canjump = 1; - - /* PPC64 hardware implements the cntlzd instruction */ - asm volatile ("cntlzd %0, %1" : "=r" (x) : "r" (x) ); +#if defined _MSC_VER +#include /* for __cpuid() and _xgetbv() */ +#elif defined __GNUC__ && defined HAVE_CPUID_H +#include /* for __get_cpuid() and __get_cpuid_max() */ +#endif - info->data.ppc.ppc64 = 1; - } - signal (SIGILL, SIG_DFL); /*@@@@@@ should save and restore old signal */ +FLAC__uint32 FLAC__cpu_have_cpuid_x86(void) +{ +#ifdef FLAC__CPU_X86_64 + return 1; +#else +# if defined _MSC_VER || defined __INTEL_COMPILER /* Do they support CPUs w/o CPUID support (or OSes that work on those CPUs)? */ + FLAC__uint32 flags1, flags2; + __asm { + pushfd + pushfd + pop eax + mov flags1, eax + xor eax, 0x200000 + push eax + popfd + pushfd + pop eax + mov flags2, eax + popfd } -# endif -# else /* !FLAC__USE_ALTIVEC */ - info->data.ppc.altivec = 0; - info->data.ppc.ppc64 = 0; -# endif + if (((flags1^flags2) & 0x200000) != 0) + return 1; + else + return 0; +# elif defined __GNUC__ && defined HAVE_CPUID_H + if (__get_cpuid_max(0, 0) != 0) + return 1; + else + return 0; # else - info->use_asm = false; + return 0; # endif +#endif +} -/* - * unknown CPI - */ +void FLAC__cpu_info_x86(FLAC__uint32 level, FLAC__uint32 *eax, FLAC__uint32 *ebx, FLAC__uint32 *ecx, FLAC__uint32 *edx) +{ + (void) level; + +#if defined _MSC_VER || defined __INTEL_COMPILER + int cpuinfo[4]; + int ext = level & 0x80000000; + __cpuid(cpuinfo, ext); + if((unsigned)cpuinfo[0] < level) { + *eax = *ebx = *ecx = *edx = 0; + return; + } +#if defined FLAC__AVX_SUPPORTED + __cpuidex(cpuinfo, level, 0); /* for AVX2 detection */ #else - info->type = FLAC__CPUINFO_TYPE_UNKNOWN; - info->use_asm = false; + __cpuid(cpuinfo, level); /* some old compilers don't support __cpuidex */ +#endif + *eax = cpuinfo[0]; *ebx = cpuinfo[1]; *ecx = cpuinfo[2]; *edx = cpuinfo[3]; +#elif defined __GNUC__ && defined HAVE_CPUID_H + FLAC__uint32 ext = level & 0x80000000; + __cpuid(ext, *eax, *ebx, *ecx, *edx); + if (*eax < level) { + *eax = *ebx = *ecx = *edx = 0; + return; + } + __cpuid_count(level, 0, *eax, *ebx, *ecx, *edx); +#else + *eax = *ebx = *ecx = *edx = 0; #endif } + +FLAC__uint32 FLAC__cpu_xgetbv_x86(void) +{ +#if (defined _MSC_VER || defined __INTEL_COMPILER) && defined FLAC__AVX_SUPPORTED + return (FLAC__uint32)_xgetbv(0); +#elif defined __GNUC__ + FLAC__uint32 lo, hi; + asm volatile (".byte 0x0f, 0x01, 0xd0" : "=a"(lo), "=d"(hi) : "c" (0)); + return lo; +#else + return 0; +#endif +} + +#endif /* (FLAC__CPU_IA32 || FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN */ diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/crc.c b/source/modules/juce_audio_formats/codecs/flac/libFLAC/crc.c index 225b26d10..f58e7be5a 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/crc.c +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/crc.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,7 +30,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H # include #endif diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/fixed.c b/source/modules/juce_audio_formats/codecs/flac/libFLAC/fixed.c index f8ebeda94..78a9ec09e 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/fixed.c +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/fixed.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,21 +30,17 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H # include #endif #include #include +#include "../compat.h" #include "include/private/bitmath.h" #include "include/private/fixed.h" #include "../assert.h" -#ifndef M_LN2 -/* math.h in VC++ doesn't seem to have this (how Microsoft is that?) */ -#define M_LN2 0.69314718055994530942 -#endif - #ifdef local_abs #undef local_abs #endif @@ -320,20 +316,11 @@ unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsig FLAC__ASSERT(data_len > 0 || total_error_3 == 0); FLAC__ASSERT(data_len > 0 || total_error_4 == 0); #ifndef FLAC__INTEGER_ONLY_LIBRARY -#if defined _MSC_VER || defined __MINGW32__ - /* with MSVC you have to spoon feed it the casting */ - residual_bits_per_sample[0] = (FLAC__float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); - residual_bits_per_sample[1] = (FLAC__float)((total_error_1 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0); - residual_bits_per_sample[2] = (FLAC__float)((total_error_2 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0); - residual_bits_per_sample[3] = (FLAC__float)((total_error_3 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0); - residual_bits_per_sample[4] = (FLAC__float)((total_error_4 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0); -#else residual_bits_per_sample[0] = (FLAC__float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); residual_bits_per_sample[1] = (FLAC__float)((total_error_1 > 0) ? log(M_LN2 * (FLAC__double)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0); residual_bits_per_sample[2] = (FLAC__float)((total_error_2 > 0) ? log(M_LN2 * (FLAC__double)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0); residual_bits_per_sample[3] = (FLAC__float)((total_error_3 > 0) ? log(M_LN2 * (FLAC__double)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0); residual_bits_per_sample[4] = (FLAC__float)((total_error_4 > 0) ? log(M_LN2 * (FLAC__double)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0); -#endif #else residual_bits_per_sample[0] = (total_error_0 > 0) ? local__compute_rbps_wide_integerized(total_error_0, data_len) : 0; residual_bits_per_sample[1] = (total_error_1 > 0) ? local__compute_rbps_wide_integerized(total_error_1, data_len) : 0; diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/float.c b/source/modules/juce_audio_formats/codecs/flac/libFLAC/float.c index ae1040712..1c16a2b41 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/float.c +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/float.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2004-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,7 +30,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H # include #endif diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/format.c b/source/modules/juce_audio_formats/codecs/flac/libFLAC/format.c index 1f37de9f8..eb8f56f7d 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/format.c +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/format.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,7 +30,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H # include #endif @@ -45,12 +45,7 @@ /* VERSION should come from configure */ FLAC_API const char *FLAC__VERSION_STRING = VERSION; -#if defined _MSC_VER || defined __BORLANDC__ || defined __MINW32__ -/* yet one more hack because of MSVC6: */ -FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC 1.3.0 20130526"; -#else -FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC " VERSION " 20130526"; -#endif +FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC " VERSION " 20141125"; FLAC_API const FLAC__byte FLAC__STREAM_SYNC_STRING[4] = { 'f','L','a','C' }; FLAC_API const unsigned FLAC__STREAM_SYNC = 0x664C6143; diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/all.h b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/all.h index 29253097e..a526071d2 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/all.h +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/all.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitmath.h b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitmath.h index 371bd488e..9ca9ec321 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitmath.h +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitmath.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2001-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -34,6 +34,7 @@ #define FLAC__PRIVATE__BITMATH_H #include "../../../ordinals.h" +#include "../../../assert.h" /* for CHAR_BIT */ #include @@ -74,16 +75,19 @@ static inline unsigned int FLAC__clz_soft_uint32(unsigned int word) static inline unsigned int FLAC__clz_uint32(FLAC__uint32 v) { /* Never used with input 0 */ + FLAC__ASSERT(v > 0); #if defined(__INTEL_COMPILER) return _bit_scan_reverse(v) ^ 31U; #elif defined(__GNUC__) && (__GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) /* This will translate either to (bsr ^ 31U), clz , ctlz, cntlz, lzcnt depending on - * -march= setting or to a software rutine in exotic machines. */ + * -march= setting or to a software routine in exotic machines. */ return __builtin_clz(v); #elif defined(_MSC_VER) && (_MSC_VER >= 1400) - FLAC__uint32 idx; - _BitScanReverse((DWORD*) &idx, v); - return idx ^ 31U; + { + unsigned long idx; + _BitScanReverse(&idx, v); + return idx ^ 31U; + } #else return FLAC__clz_soft_uint32(v); #endif @@ -99,7 +103,7 @@ static inline unsigned int FLAC__clz2_uint32(FLAC__uint32 v) /* An example of what FLAC__bitmath_ilog2() computes: * - * ilog2( 0) = undefined + * ilog2( 0) = assertion failure * ilog2( 1) = 0 * ilog2( 2) = 1 * ilog2( 3) = 1 @@ -122,45 +126,56 @@ static inline unsigned int FLAC__clz2_uint32(FLAC__uint32 v) static inline unsigned FLAC__bitmath_ilog2(FLAC__uint32 v) { + FLAC__ASSERT(v > 0); +#if defined(__INTEL_COMPILER) + return _bit_scan_reverse(v); +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) + { + unsigned long idx; + _BitScanReverse(&idx, v); + return idx; + } +#else return sizeof(FLAC__uint32) * CHAR_BIT - 1 - FLAC__clz_uint32(v); +#endif } -#ifdef FLAC__INTEGER_ONLY_LIBRARY /*Unused otherwise */ +#ifdef FLAC__INTEGER_ONLY_LIBRARY /* Unused otherwise */ static inline unsigned FLAC__bitmath_ilog2_wide(FLAC__uint64 v) { - if (v == 0) - return 0; + FLAC__ASSERT(v > 0); #if defined(__GNUC__) && (__GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) return sizeof(FLAC__uint64) * CHAR_BIT - 1 - __builtin_clzll(v); -/* Sorry, only supported in win64/Itanium.. */ -#elif (defined(_MSC_VER) && (_MSC_VER >= 1400)) && (defined(_M_IA64) || defined(_WIN64)) - FLAC__uint64 idx; - _BitScanReverse64(&idx, v); - return idx ^ 63U; +/* Sorry, only supported in x64/Itanium.. and both have fast FPU which makes integer-only encoder pointless */ +#elif (defined(_MSC_VER) && (_MSC_VER >= 1400)) && (defined(_M_IA64) || defined(_M_X64)) + { + unsigned long idx; + _BitScanReverse64(&idx, v); + return idx; + } #else -/* Brain-damaged compilers will use the fastest possible way that is, +/* Brain-damaged compilers will use the fastest possible way that is, de Bruijn sequences (http://supertech.csail.mit.edu/papers/debruijn.pdf) - (C) Timothy B. Terriberry (tterribe@xiph.org) 2001-2009 LGPL (v2 or later). + (C) Timothy B. Terriberry (tterribe@xiph.org) 2001-2009 CC0 (Public domain). */ - static const unsigned char DEBRUIJN_IDX64[64]={ - 0, 1, 2, 7, 3,13, 8,19, 4,25,14,28, 9,34,20,40, - 5,17,26,38,15,46,29,48,10,31,35,54,21,50,41,57, - 63, 6,12,18,24,27,33,39,16,37,45,47,30,53,49,56, - 62,11,23,32,36,44,52,55,61,22,43,51,60,42,59,58 - }; - int ret; - ret= v>0; - v|= v>>1; - v|= v>>2; - v|= v>>4; - v|= v>>8; - v|= v>>16; - v|= v>>32; - v= (v>>1)+1; - ret+=DEBRUIJN_IDX64[v*0x218A392CD3D5DBF>>58&0x3F]; - return ret; + { + static const unsigned char DEBRUIJN_IDX64[64]={ + 0, 1, 2, 7, 3,13, 8,19, 4,25,14,28, 9,34,20,40, + 5,17,26,38,15,46,29,48,10,31,35,54,21,50,41,57, + 63, 6,12,18,24,27,33,39,16,37,45,47,30,53,49,56, + 62,11,23,32,36,44,52,55,61,22,43,51,60,42,59,58 + }; + v|= v>>1; + v|= v>>2; + v|= v>>4; + v|= v>>8; + v|= v>>16; + v|= v>>32; + v= (v>>1)+1; + return DEBRUIJN_IDX64[v*0x218A392CD3D5DBF>>58&0x3F]; + } #endif } #endif diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitreader.h b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitreader.h index 4d9668fff..83f8361ee 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitreader.h +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitreader.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -50,7 +50,7 @@ typedef FLAC__bool (*FLAC__BitReaderReadCallback)(FLAC__byte buffer[], size_t *b */ FLAC__BitReader *FLAC__bitreader_new(void); void FLAC__bitreader_delete(FLAC__BitReader *br); -FLAC__bool FLAC__bitreader_init(FLAC__BitReader *br, FLAC__CPUInfo cpu, FLAC__BitReaderReadCallback rcb, void *cd); +FLAC__bool FLAC__bitreader_init(FLAC__BitReader *br, FLAC__BitReaderReadCallback rcb, void *cd); void FLAC__bitreader_free(FLAC__BitReader *br); /* does not 'free(br)' */ FLAC__bool FLAC__bitreader_clear(FLAC__BitReader *br); void FLAC__bitreader_dump(const FLAC__BitReader *br, FILE *out); @@ -82,19 +82,10 @@ FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, F FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, unsigned *val); FLAC__bool FLAC__bitreader_read_rice_signed(FLAC__BitReader *br, int *val, unsigned parameter); FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter); -#ifndef FLAC__NO_ASM -# ifdef FLAC__CPU_IA32 -# ifdef FLAC__HAS_NASM -FLAC__bool FLAC__bitreader_read_rice_signed_block_asm_ia32_bswap(FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter); -# endif -# endif -#endif #if 0 /* UNUSED */ FLAC__bool FLAC__bitreader_read_golomb_signed(FLAC__BitReader *br, int *val, unsigned parameter); FLAC__bool FLAC__bitreader_read_golomb_unsigned(FLAC__BitReader *br, unsigned *val, unsigned parameter); #endif FLAC__bool FLAC__bitreader_read_utf8_uint32(FLAC__BitReader *br, FLAC__uint32 *val, FLAC__byte *raw, unsigned *rawlen); FLAC__bool FLAC__bitreader_read_utf8_uint64(FLAC__BitReader *br, FLAC__uint64 *val, FLAC__byte *raw, unsigned *rawlen); - -FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br); #endif diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitwriter.h b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitwriter.h index b1ff5e6ee..1e23efe5f 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitwriter.h +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitwriter.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/cpu.h b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/cpu.h index 930d67807..655800e8a 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/cpu.h +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/cpu.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2001-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -41,49 +41,59 @@ typedef enum { FLAC__CPUINFO_TYPE_IA32, - FLAC__CPUINFO_TYPE_PPC, + FLAC__CPUINFO_TYPE_X86_64, FLAC__CPUINFO_TYPE_UNKNOWN } FLAC__CPUInfo_Type; +#if defined FLAC__CPU_IA32 typedef struct { - FLAC__bool cpuid; - FLAC__bool bswap; FLAC__bool cmov; FLAC__bool mmx; - FLAC__bool fxsr; FLAC__bool sse; FLAC__bool sse2; + FLAC__bool sse3; FLAC__bool ssse3; - FLAC__bool _3dnow; - FLAC__bool ext3dnow; - FLAC__bool extmmx; + FLAC__bool sse41; + FLAC__bool sse42; + FLAC__bool avx; + FLAC__bool avx2; + FLAC__bool fma; } FLAC__CPUInfo_IA32; - +#elif defined FLAC__CPU_X86_64 typedef struct { - FLAC__bool altivec; - FLAC__bool ppc64; -} FLAC__CPUInfo_PPC; + FLAC__bool sse3; + FLAC__bool ssse3; + FLAC__bool sse41; + FLAC__bool sse42; + FLAC__bool avx; + FLAC__bool avx2; + FLAC__bool fma; +} FLAC__CPUInfo_x86; +#endif typedef struct { FLAC__bool use_asm; FLAC__CPUInfo_Type type; - union { - FLAC__CPUInfo_IA32 ia32; - FLAC__CPUInfo_PPC ppc; - } data; +#if defined FLAC__CPU_IA32 + FLAC__CPUInfo_IA32 ia32; +#elif defined FLAC__CPU_X86_64 + FLAC__CPUInfo_x86 x86; +#endif } FLAC__CPUInfo; void FLAC__cpu_info(FLAC__CPUInfo *info); #ifndef FLAC__NO_ASM -#ifdef FLAC__CPU_IA32 -#ifdef FLAC__HAS_NASM +# if defined FLAC__CPU_IA32 && defined FLAC__HAS_NASM FLAC__uint32 FLAC__cpu_have_cpuid_asm_ia32(void); void FLAC__cpu_info_asm_ia32(FLAC__uint32 *flags_edx, FLAC__uint32 *flags_ecx); -FLAC__uint32 FLAC__cpu_info_extended_amd_asm_ia32(void); -#endif -#endif +# endif +# if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && defined FLAC__HAS_X86INTRIN +FLAC__uint32 FLAC__cpu_have_cpuid_x86(void); +void FLAC__cpu_info_x86(FLAC__uint32 level, FLAC__uint32 *eax, FLAC__uint32 *ebx, FLAC__uint32 *ecx, FLAC__uint32 *edx); +FLAC__uint32 FLAC__cpu_xgetbv_x86(void); +# endif #endif #endif diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/crc.h b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/crc.h index 62847831d..8ebe5c8f7 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/crc.h +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/crc.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -51,7 +51,7 @@ FLAC__uint8 FLAC__crc8(const FLAC__byte *data, unsigned len); */ extern unsigned const FLAC__crc16_table[256]; -#define FLAC__CRC16_UPDATE(data, crc) (((((crc)<<8) & 0xffff) ^ FLAC__crc16_table[((crc)>>8) ^ (data)])) +#define FLAC__CRC16_UPDATE(data, crc) ((((crc)<<8) & 0xffff) ^ FLAC__crc16_table[((crc)>>8) ^ (data)]) /* this alternate may be faster on some systems/compilers */ #if 0 #define FLAC__CRC16_UPDATE(data, crc) ((((crc)<<8) ^ FLAC__crc16_table[((crc)>>8) ^ (data)]) & 0xffff) diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/fixed.h b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/fixed.h index 0123bed34..e4c044b57 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/fixed.h +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/fixed.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,6 +37,7 @@ #include #endif +#include "cpu.h" #include "float.h" #include "../../../format.h" @@ -54,14 +55,22 @@ */ #ifndef FLAC__INTEGER_ONLY_LIBRARY unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); # ifndef FLAC__NO_ASM -# ifdef FLAC__CPU_IA32 -# ifdef FLAC__HAS_NASM -unsigned FLAC__fixed_compute_best_predictor_asm_ia32_mmx_cmov(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +# if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && defined FLAC__HAS_X86INTRIN +# ifdef FLAC__SSE2_SUPPORTED +unsigned FLAC__fixed_compute_best_predictor_intrin_sse2(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER + 1]); +unsigned FLAC__fixed_compute_best_predictor_wide_intrin_sse2(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER + 1]); +# endif +# ifdef FLAC__SSSE3_SUPPORTED +unsigned FLAC__fixed_compute_best_predictor_intrin_ssse3(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +unsigned FLAC__fixed_compute_best_predictor_wide_intrin_ssse3(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER + 1]); # endif # endif +# if defined FLAC__CPU_IA32 && defined FLAC__HAS_NASM +unsigned FLAC__fixed_compute_best_predictor_asm_ia32_mmx_cmov(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +# endif # endif -unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); #else unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/float.h b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/float.h index 74d17ed22..af09336c2 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/float.h +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/float.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2004-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/format.h b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/format.h index 996c7e946..87fb9e139 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/format.h +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/format.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/lpc.h b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/lpc.h index d639e25b9..d97c26be7 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/lpc.h +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/lpc.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,6 +37,7 @@ #include #endif +#include "cpu.h" #include "float.h" #include "../../../format.h" @@ -75,7 +76,15 @@ void FLAC__lpc_compute_autocorrelation_asm_ia32(const FLAC__real data[], unsigne void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_4(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_8(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_12(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); -void FLAC__lpc_compute_autocorrelation_asm_ia32_3dnow(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_16(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +# endif +# endif +# if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && defined FLAC__HAS_X86INTRIN +# ifdef FLAC__SSE_SUPPORTED +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_4(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_8(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_12(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_16(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); # endif # endif #endif @@ -145,6 +154,22 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 *da # ifdef FLAC__HAS_NASM void FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); void FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_asm_ia32(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +# endif +# endif +# if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && defined FLAC__HAS_X86INTRIN +# ifdef FLAC__SSE2_SUPPORTED +void FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_sse2(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_sse2(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +# endif +# ifdef FLAC__SSE4_1_SUPPORTED +void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_sse41(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_sse41(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +# endif +# ifdef FLAC__AVX2_SUPPORTED +void FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_avx2(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_avx2(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_avx2(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); # endif # endif #endif @@ -173,11 +198,17 @@ void FLAC__lpc_restore_signal_wide(const FLAC__int32 residual[], unsigned data_l # ifdef FLAC__HAS_NASM void FLAC__lpc_restore_signal_asm_ia32(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); void FLAC__lpc_restore_signal_asm_ia32_mmx(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +void FLAC__lpc_restore_signal_wide_asm_ia32(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); # endif /* FLAC__HAS_NASM */ -# elif defined FLAC__CPU_PPC -void FLAC__lpc_restore_signal_asm_ppc_altivec_16(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); -void FLAC__lpc_restore_signal_asm_ppc_altivec_16_order8(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); -# endif/* FLAC__CPU_IA32 || FLAC__CPU_PPC */ +# endif /* FLAC__CPU_IA32 */ +# if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && defined FLAC__HAS_X86INTRIN +# ifdef FLAC__SSE2_SUPPORTED +void FLAC__lpc_restore_signal_16_intrin_sse2(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +# endif +# ifdef FLAC__SSE4_1_SUPPORTED +void FLAC__lpc_restore_signal_wide_intrin_sse41(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +# endif +# endif #endif /* FLAC__NO_ASM */ #ifndef FLAC__INTEGER_ONLY_LIBRARY diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/md5.h b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/md5.h index 2cf5c5dad..b1324a93f 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/md5.h +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/md5.h @@ -28,11 +28,17 @@ #include "../../../ordinals.h" +typedef union { + FLAC__byte *p8; + FLAC__int16 *p16; + FLAC__int32 *p32; +} FLAC__multibyte; + typedef struct { FLAC__uint32 in[16]; FLAC__uint32 buf[4]; FLAC__uint32 bytes[2]; - FLAC__byte *internal_buf; + FLAC__multibyte internal_buf; size_t capacity; } FLAC__MD5Context; diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/memory.h b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/memory.h index d6651c609..c387ea6be 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/memory.h +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/memory.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2001-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/metadata.h b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/metadata.h index a29050eda..29c73e0fd 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/metadata.h +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/metadata.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2002-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/stream_encoder.h b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/stream_encoder.h new file mode 100644 index 000000000..3ba6dd2d6 --- /dev/null +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/stream_encoder.h @@ -0,0 +1,67 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2014 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__STREAM_ENCODER_H +#define FLAC__PRIVATE__STREAM_ENCODER_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* + * This is used to avoid overflow with unusual signals in 32-bit + * accumulator in the *precompute_partition_info_sums_* functions. + */ +#define FLAC__MAX_EXTRA_RESIDUAL_BPS 4 + +#if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && defined FLAC__HAS_X86INTRIN +#include "cpu.h" +#include "../../../format.h" + +#ifdef FLAC__SSE2_SUPPORTED +extern void FLAC__precompute_partition_info_sums_intrin_sse2(const FLAC__int32 residual[], FLAC__uint64 abs_residual_partition_sums[], + unsigned residual_samples, unsigned predictor_order, unsigned min_partition_order, unsigned max_partition_order, unsigned bps); +#endif + +#ifdef FLAC__SSSE3_SUPPORTED +extern void FLAC__precompute_partition_info_sums_intrin_ssse3(const FLAC__int32 residual[], FLAC__uint64 abs_residual_partition_sums[], + unsigned residual_samples, unsigned predictor_order, unsigned min_partition_order, unsigned max_partition_order, unsigned bps); +#endif + +#ifdef FLAC__AVX2_SUPPORTED +extern void FLAC__precompute_partition_info_sums_intrin_avx2(const FLAC__int32 residual[], FLAC__uint64 abs_residual_partition_sums[], + unsigned residual_samples, unsigned predictor_order, unsigned min_partition_order, unsigned max_partition_order, unsigned bps); +#endif + +#endif + +#endif diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/stream_encoder_framing.h b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/stream_encoder_framing.h index 51bf8c357..eaa9958c7 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/stream_encoder_framing.h +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/stream_encoder_framing.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/window.h b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/window.h index 8acec39ee..8464d22db 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/window.h +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/window.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2006-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -65,6 +65,8 @@ void FLAC__window_nuttall(FLAC__real *window, const FLAC__int32 L); void FLAC__window_rectangle(FLAC__real *window, const FLAC__int32 L); void FLAC__window_triangle(FLAC__real *window, const FLAC__int32 L); void FLAC__window_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p); +void FLAC__window_partial_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p, const FLAC__real start, const FLAC__real end); +void FLAC__window_punchout_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p, const FLAC__real start, const FLAC__real end); void FLAC__window_welch(FLAC__real *window, const FLAC__int32 L); #endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/all.h b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/all.h index 057cd73fa..b852c2b53 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/all.h +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/all.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2001-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_decoder.h b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_decoder.h index 0d5813fc6..d8a3e6194 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_decoder.h +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_decoder.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -40,6 +40,7 @@ typedef struct FLAC__StreamDecoderProtected { FLAC__StreamDecoderState state; + FLAC__StreamDecoderInitStatus initstate; unsigned channels; FLAC__ChannelAssignment channel_assignment; unsigned bits_per_sample; diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_encoder.h b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_encoder.h index 2c632f955..bd0cf25bf 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_encoder.h +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_encoder.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2001-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -59,6 +59,8 @@ typedef enum { FLAC__APODIZATION_RECTANGLE, FLAC__APODIZATION_TRIANGLE, FLAC__APODIZATION_TUKEY, + FLAC__APODIZATION_PARTIAL_TUKEY, + FLAC__APODIZATION_PUNCHOUT_TUKEY, FLAC__APODIZATION_WELCH } FLAC__ApodizationFunction; @@ -71,6 +73,11 @@ typedef struct { struct { FLAC__real p; } tukey; + struct { + FLAC__real p; + FLAC__real start; + FLAC__real end; + } multiple_tukey; } parameters; } FLAC__ApodizationSpecification; diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/lpc_flac.c b/source/modules/juce_audio_formats/codecs/flac/libFLAC/lpc_flac.c index 5741e7c2b..87e2321e6 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/lpc_flac.c +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/lpc_flac.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,7 +30,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H # include #endif @@ -50,11 +50,6 @@ #ifndef FLAC__INTEGER_ONLY_LIBRARY -#ifndef M_LN2 -/* math.h in VC++ doesn't seem to have this (how Microsoft is that?) */ -#define M_LN2 0.69314718055994530942 -#endif - #if !defined(HAVE_LROUND) #if defined(_MSC_VER) #include @@ -65,7 +60,7 @@ static inline long int lround(double x) { return (long)(x + copysign (0.5, x)); } -//If this fails, we are in the precence of a mid 90's compiler..move along... +/* If this fails, we are in the presence of a mid 90's compiler, move along... */ #endif void FLAC__lpc_window_data(const FLAC__int32 in[], const FLAC__real window[], FLAC__real out[], unsigned data_len) @@ -160,7 +155,7 @@ void FLAC__lpc_compute_lp_coefficients(const FLAC__real autoc[], unsigned *max_o lp_coeff[i][j] = (FLAC__real)(-lpc[j]); /* negate FIR filter coeff to get predictor coeff */ error[i] = err; - /* see SF bug #1601812 http://sourceforge.net/tracker/index.php?func=detail&aid=1601812&group_id=13478&atid=113478 */ + /* see SF bug https://sourceforge.net/p/flac/bugs/234/ */ if(err == 0.0) { *max_order = i+1; return; @@ -264,7 +259,12 @@ int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, return 0; } -void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]) +#if defined(_MSC_VER) +// silence MSVC warnings about __restrict modifier +#pragma warning ( disable : 4028 ) +#endif + +void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 * flac_restrict data, unsigned data_len, const FLAC__int32 * flac_restrict qlp_coeff, unsigned order, int lp_quantization, FLAC__int32 * flac_restrict residual) #if defined(FLAC__OVERFLOW_DETECT) || !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) { FLAC__int64 sumo; @@ -524,7 +524,7 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 *data, u } #endif -void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]) +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 * flac_restrict data, unsigned data_len, const FLAC__int32 * flac_restrict qlp_coeff, unsigned order, int lp_quantization, FLAC__int32 * flac_restrict residual) #if defined(FLAC__OVERFLOW_DETECT) || !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) { unsigned i, j; @@ -780,7 +780,7 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 *da #endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ -void FLAC__lpc_restore_signal(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]) +void FLAC__lpc_restore_signal(const FLAC__int32 * flac_restrict residual, unsigned data_len, const FLAC__int32 * flac_restrict qlp_coeff, unsigned order, int lp_quantization, FLAC__int32 * flac_restrict data) #if defined(FLAC__OVERFLOW_DETECT) || !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) { FLAC__int64 sumo; @@ -1041,7 +1041,7 @@ void FLAC__lpc_restore_signal(const FLAC__int32 residual[], unsigned data_len, c } #endif -void FLAC__lpc_restore_signal_wide(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]) +void FLAC__lpc_restore_signal_wide(const FLAC__int32 * flac_restrict residual, unsigned data_len, const FLAC__int32 * flac_restrict qlp_coeff, unsigned order, int lp_quantization, FLAC__int32 * flac_restrict data) #if defined(FLAC__OVERFLOW_DETECT) || !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) { unsigned i, j; @@ -1295,6 +1295,10 @@ void FLAC__lpc_restore_signal_wide(const FLAC__int32 residual[], unsigned data_l } #endif +#if defined(_MSC_VER) +#pragma warning ( default : 4028 ) +#endif + #ifndef FLAC__INTEGER_ONLY_LIBRARY FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample(FLAC__double lpc_error, unsigned total_samples) diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/md5.c b/source/modules/juce_audio_formats/codecs/flac/libFLAC/md5.c index c448b87d3..d41f6a810 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/md5.c +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/md5.c @@ -1,4 +1,4 @@ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H # include #endif @@ -7,6 +7,7 @@ #include "include/private/md5.h" #include "../alloc.h" +#include "../endswap.h" /* * This code implements the MD5 message-digest algorithm. @@ -223,7 +224,7 @@ void FLAC__MD5Init(FLAC__MD5Context *ctx) ctx->bytes[0] = 0; ctx->bytes[1] = 0; - ctx->internal_buf = 0; + ctx->internal_buf.p8= 0; ctx->capacity = 0; } @@ -259,9 +260,9 @@ void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *ctx) byteSwap(ctx->buf, 4); memcpy(digest, ctx->buf, 16); - if(0 != ctx->internal_buf) { - free(ctx->internal_buf); - ctx->internal_buf = 0; + if (0 != ctx->internal_buf.p8) { + free(ctx->internal_buf.p8); + ctx->internal_buf.p8= 0; ctx->capacity = 0; } memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ @@ -270,58 +271,124 @@ void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *ctx) /* * Convert the incoming audio signal to a byte stream */ -static void format_input_(FLAC__byte *buf, const FLAC__int32 * const signal[], unsigned channels, unsigned samples, unsigned bytes_per_sample) +static void format_input_(FLAC__multibyte *mbuf, const FLAC__int32 * const signal[], unsigned channels, unsigned samples, unsigned bytes_per_sample) { + FLAC__byte *buf_ = mbuf->p8; + FLAC__int16 *buf16 = mbuf->p16; + FLAC__int32 *buf32 = mbuf->p32; + FLAC__int32 a_word; unsigned channel, sample; - register FLAC__int32 a_word; - register FLAC__byte *buf_ = buf; -#if WORDS_BIGENDIAN -#else - if(channels == 2 && bytes_per_sample == 2) { - FLAC__int16 *buf1_ = ((FLAC__int16*)buf_) + 1; - memcpy(buf_, signal[0], sizeof(FLAC__int32) * samples); - for(sample = 0; sample < samples; sample++, buf1_+=2) - *buf1_ = (FLAC__int16)signal[1][sample]; - } - else if(channels == 1 && bytes_per_sample == 2) { - FLAC__int16 *buf1_ = (FLAC__int16*)buf_; - for(sample = 0; sample < samples; sample++) - *buf1_++ = (FLAC__int16)signal[0][sample]; - } - else -#endif - if(bytes_per_sample == 2) { - if(channels == 2) { - for(sample = 0; sample < samples; sample++) { - a_word = signal[0][sample]; - *buf_++ = (FLAC__byte)a_word; a_word >>= 8; - *buf_++ = (FLAC__byte)a_word; - a_word = signal[1][sample]; - *buf_++ = (FLAC__byte)a_word; a_word >>= 8; - *buf_++ = (FLAC__byte)a_word; + /* Storage in the output buffer, buf, is little endian. */ + +#define BYTES_CHANNEL_SELECTOR(bytes, channels) (bytes * 100 + channels) + + /* First do the most commonly used combinations. */ + switch (BYTES_CHANNEL_SELECTOR (bytes_per_sample, channels)) { + /* One byte per sample. */ + case (BYTES_CHANNEL_SELECTOR (1, 1)): + for (sample = 0; sample < samples; sample++) + *buf_++ = signal[0][sample]; + return; + + case (BYTES_CHANNEL_SELECTOR (1, 2)): + for (sample = 0; sample < samples; sample++) { + *buf_++ = signal[0][sample]; + *buf_++ = signal[1][sample]; } - } - else if(channels == 1) { - for(sample = 0; sample < samples; sample++) { + return; + + case (BYTES_CHANNEL_SELECTOR (1, 4)): + for (sample = 0; sample < samples; sample++) { + *buf_++ = signal[0][sample]; + *buf_++ = signal[1][sample]; + *buf_++ = signal[2][sample]; + *buf_++ = signal[3][sample]; + } + return; + + case (BYTES_CHANNEL_SELECTOR (1, 6)): + for (sample = 0; sample < samples; sample++) { + *buf_++ = signal[0][sample]; + *buf_++ = signal[1][sample]; + *buf_++ = signal[2][sample]; + *buf_++ = signal[3][sample]; + *buf_++ = signal[4][sample]; + *buf_++ = signal[5][sample]; + } + return; + + case (BYTES_CHANNEL_SELECTOR (1, 8)): + for (sample = 0; sample < samples; sample++) { + *buf_++ = signal[0][sample]; + *buf_++ = signal[1][sample]; + *buf_++ = signal[2][sample]; + *buf_++ = signal[3][sample]; + *buf_++ = signal[4][sample]; + *buf_++ = signal[5][sample]; + *buf_++ = signal[6][sample]; + *buf_++ = signal[7][sample]; + } + return; + + /* Two bytes per sample. */ + case (BYTES_CHANNEL_SELECTOR (2, 1)): + for (sample = 0; sample < samples; sample++) + *buf16++ = H2LE_16(signal[0][sample]); + return; + + case (BYTES_CHANNEL_SELECTOR (2, 2)): + for (sample = 0; sample < samples; sample++) { + *buf16++ = H2LE_16(signal[0][sample]); + *buf16++ = H2LE_16(signal[1][sample]); + } + return; + + case (BYTES_CHANNEL_SELECTOR (2, 4)): + for (sample = 0; sample < samples; sample++) { + *buf16++ = H2LE_16(signal[0][sample]); + *buf16++ = H2LE_16(signal[1][sample]); + *buf16++ = H2LE_16(signal[2][sample]); + *buf16++ = H2LE_16(signal[3][sample]); + } + return; + + case (BYTES_CHANNEL_SELECTOR (2, 6)): + for (sample = 0; sample < samples; sample++) { + *buf16++ = H2LE_16(signal[0][sample]); + *buf16++ = H2LE_16(signal[1][sample]); + *buf16++ = H2LE_16(signal[2][sample]); + *buf16++ = H2LE_16(signal[3][sample]); + *buf16++ = H2LE_16(signal[4][sample]); + *buf16++ = H2LE_16(signal[5][sample]); + } + return; + + case (BYTES_CHANNEL_SELECTOR (2, 8)): + for (sample = 0; sample < samples; sample++) { + *buf16++ = H2LE_16(signal[0][sample]); + *buf16++ = H2LE_16(signal[1][sample]); + *buf16++ = H2LE_16(signal[2][sample]); + *buf16++ = H2LE_16(signal[3][sample]); + *buf16++ = H2LE_16(signal[4][sample]); + *buf16++ = H2LE_16(signal[5][sample]); + *buf16++ = H2LE_16(signal[6][sample]); + *buf16++ = H2LE_16(signal[7][sample]); + } + return; + + /* Three bytes per sample. */ + case (BYTES_CHANNEL_SELECTOR (3, 1)): + for (sample = 0; sample < samples; sample++) { a_word = signal[0][sample]; *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; *buf_++ = (FLAC__byte)a_word; } - } - else { - for(sample = 0; sample < samples; sample++) { - for(channel = 0; channel < channels; channel++) { - a_word = signal[channel][sample]; - *buf_++ = (FLAC__byte)a_word; a_word >>= 8; - *buf_++ = (FLAC__byte)a_word; - } - } - } - } - else if(bytes_per_sample == 3) { - if(channels == 2) { - for(sample = 0; sample < samples; sample++) { + return; + + case (BYTES_CHANNEL_SELECTOR (3, 2)): + for (sample = 0; sample < samples; sample++) { a_word = signal[0][sample]; *buf_++ = (FLAC__byte)a_word; a_word >>= 8; *buf_++ = (FLAC__byte)a_word; a_word >>= 8; @@ -331,60 +398,90 @@ static void format_input_(FLAC__byte *buf, const FLAC__int32 * const signal[], u *buf_++ = (FLAC__byte)a_word; a_word >>= 8; *buf_++ = (FLAC__byte)a_word; } - } - else if(channels == 1) { - for(sample = 0; sample < samples; sample++) { - a_word = signal[0][sample]; - *buf_++ = (FLAC__byte)a_word; a_word >>= 8; - *buf_++ = (FLAC__byte)a_word; a_word >>= 8; - *buf_++ = (FLAC__byte)a_word; + return; + + /* Four bytes per sample. */ + case (BYTES_CHANNEL_SELECTOR (4, 1)): + for (sample = 0; sample < samples; sample++) + *buf32++ = H2LE_32(signal[0][sample]); + return; + + case (BYTES_CHANNEL_SELECTOR (4, 2)): + for (sample = 0; sample < samples; sample++) { + *buf32++ = H2LE_32(signal[0][sample]); + *buf32++ = H2LE_32(signal[1][sample]); } - } - else { - for(sample = 0; sample < samples; sample++) { - for(channel = 0; channel < channels; channel++) { - a_word = signal[channel][sample]; - *buf_++ = (FLAC__byte)a_word; a_word >>= 8; - *buf_++ = (FLAC__byte)a_word; a_word >>= 8; - *buf_++ = (FLAC__byte)a_word; - } + return; + + case (BYTES_CHANNEL_SELECTOR (4, 4)): + for (sample = 0; sample < samples; sample++) { + *buf32++ = H2LE_32(signal[0][sample]); + *buf32++ = H2LE_32(signal[1][sample]); + *buf32++ = H2LE_32(signal[2][sample]); + *buf32++ = H2LE_32(signal[3][sample]); } - } - } - else if(bytes_per_sample == 1) { - if(channels == 2) { - for(sample = 0; sample < samples; sample++) { - a_word = signal[0][sample]; - *buf_++ = (FLAC__byte)a_word; - a_word = signal[1][sample]; - *buf_++ = (FLAC__byte)a_word; + return; + + case (BYTES_CHANNEL_SELECTOR (4, 6)): + for (sample = 0; sample < samples; sample++) { + *buf32++ = H2LE_32(signal[0][sample]); + *buf32++ = H2LE_32(signal[1][sample]); + *buf32++ = H2LE_32(signal[2][sample]); + *buf32++ = H2LE_32(signal[3][sample]); + *buf32++ = H2LE_32(signal[4][sample]); + *buf32++ = H2LE_32(signal[5][sample]); } - } - else if(channels == 1) { - for(sample = 0; sample < samples; sample++) { - a_word = signal[0][sample]; - *buf_++ = (FLAC__byte)a_word; + return; + + case (BYTES_CHANNEL_SELECTOR (4, 8)): + for (sample = 0; sample < samples; sample++) { + *buf32++ = H2LE_32(signal[0][sample]); + *buf32++ = H2LE_32(signal[1][sample]); + *buf32++ = H2LE_32(signal[2][sample]); + *buf32++ = H2LE_32(signal[3][sample]); + *buf32++ = H2LE_32(signal[4][sample]); + *buf32++ = H2LE_32(signal[5][sample]); + *buf32++ = H2LE_32(signal[6][sample]); + *buf32++ = H2LE_32(signal[7][sample]); } - } - else { - for(sample = 0; sample < samples; sample++) { - for(channel = 0; channel < channels; channel++) { + return; + + default: + break; + } + + /* General version. */ + switch (bytes_per_sample) { + case 1: + for (sample = 0; sample < samples; sample++) + for (channel = 0; channel < channels; channel++) + *buf_++ = signal[channel][sample]; + return; + + case 2: + for (sample = 0; sample < samples; sample++) + for (channel = 0; channel < channels; channel++) + *buf16++ = H2LE_16(signal[channel][sample]); + return; + + case 3: + for (sample = 0; sample < samples; sample++) + for (channel = 0; channel < channels; channel++) { a_word = signal[channel][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; *buf_++ = (FLAC__byte)a_word; } - } - } - } - else { /* bytes_per_sample == 4, maybe optimize more later */ - for(sample = 0; sample < samples; sample++) { - for(channel = 0; channel < channels; channel++) { - a_word = signal[channel][sample]; - *buf_++ = (FLAC__byte)a_word; a_word >>= 8; - *buf_++ = (FLAC__byte)a_word; a_word >>= 8; - *buf_++ = (FLAC__byte)a_word; a_word >>= 8; - *buf_++ = (FLAC__byte)a_word; - } - } + return; + + case 4: + for (sample = 0; sample < samples; sample++) + for (channel = 0; channel < channels; channel++) + *buf32++ = H2LE_32(signal[channel][sample]); + return; + + default: + break; } } @@ -396,26 +493,26 @@ FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const const size_t bytes_needed = (size_t)channels * (size_t)samples * (size_t)bytes_per_sample; /* overflow check */ - if((size_t)channels > SIZE_MAX / (size_t)bytes_per_sample) + if ((size_t)channels > SIZE_MAX / (size_t)bytes_per_sample) return false; - if((size_t)channels * (size_t)bytes_per_sample > SIZE_MAX / (size_t)samples) + if ((size_t)channels * (size_t)bytes_per_sample > SIZE_MAX / (size_t)samples) return false; - if(ctx->capacity < bytes_needed) { - FLAC__byte *tmp = (FLAC__byte*) realloc(ctx->internal_buf, bytes_needed); - if(0 == tmp) { - free(ctx->internal_buf); - if(0 == (ctx->internal_buf = (FLAC__byte*) safe_malloc_(bytes_needed))) + if (ctx->capacity < bytes_needed) { + FLAC__byte *tmp = (FLAC__byte*) realloc(ctx->internal_buf.p8, bytes_needed); + if (0 == tmp) { + free(ctx->internal_buf.p8); + if (0 == (ctx->internal_buf.p8= (FLAC__byte*) safe_malloc_(bytes_needed))) return false; } else - ctx->internal_buf = tmp; + ctx->internal_buf.p8= tmp; ctx->capacity = bytes_needed; } - format_input_(ctx->internal_buf, signal, channels, samples, bytes_per_sample); + format_input_(&ctx->internal_buf, signal, channels, samples, bytes_per_sample); - FLAC__MD5Update(ctx, ctx->internal_buf, bytes_needed); + FLAC__MD5Update(ctx, ctx->internal_buf.p8, bytes_needed); return true; } diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/memory.c b/source/modules/juce_audio_formats/codecs/flac/libFLAC/memory.c index dd972e071..fb125f169 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/memory.c +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/memory.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2001-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,10 +30,14 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H # include #endif +#ifdef HAVE_STDINT_H +#include +#endif + #include "include/private/memory.h" #include "../assert.h" #include "../alloc.h" @@ -46,25 +50,8 @@ void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address) #ifdef FLAC__ALIGN_MALLOC_DATA /* align on 32-byte (256-bit) boundary */ - x = safe_malloc_add_2op_(bytes, /*+*/31); -#ifdef SIZEOF_VOIDP -#if SIZEOF_VOIDP == 4 - /* could do *aligned_address = x + ((unsigned) (32 - (((unsigned)x) & 31))) & 31; */ - *aligned_address = (void*)(((unsigned)x + 31) & -32); -#elif SIZEOF_VOIDP == 8 - *aligned_address = (void*)(((FLAC__uint64)x + 31) & (FLAC__uint64)(-((FLAC__int64)32))); -#else -# error Unsupported sizeof(void*) -#endif -#else - /* there's got to be a better way to do this right for all archs */ - if(sizeof(void*) == sizeof(unsigned)) - *aligned_address = (void*)(((unsigned)x + 31) & -32); - else if(sizeof(void*) == sizeof(FLAC__uint64)) - *aligned_address = (void*)(((FLAC__uint64)x + 31) & (FLAC__uint64)(-((FLAC__int64)32))); - else - return 0; -#endif + x = safe_malloc_add_2op_(bytes, /*+*/31L); + *aligned_address = (void*)(((uintptr_t)x + 31L) & -32L); #else x = safe_malloc_(bytes); *aligned_address = x; diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/stream_decoder.c b/source/modules/juce_audio_formats/codecs/flac/libFLAC/stream_decoder.c index 04e1766ed..d6a79737c 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/stream_decoder.c +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/stream_decoder.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,7 +30,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H # include #endif @@ -70,7 +70,7 @@ FLAC_API int FLAC_API_SUPPORTS_OGG_FLAC = * ***********************************************************************/ -static FLAC__byte ID3V2_TAG_[3] = { 'I', 'D', '3' }; +static const FLAC__byte ID3V2_TAG_[3] = { 'I', 'D', '3' }; /*********************************************************************** * @@ -86,7 +86,7 @@ static FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder); static FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder); static FLAC__bool read_metadata_streaminfo_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length); static FLAC__bool read_metadata_seektable_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length); -static FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_VorbisComment *obj); +static FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_VorbisComment *obj, unsigned length); static FLAC__bool read_metadata_cuesheet_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_CueSheet *obj); static FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_Picture *obj); static FLAC__bool skip_id3v2_tag_(FLAC__StreamDecoder *decoder); @@ -141,9 +141,6 @@ typedef struct FLAC__StreamDecoderPrivate { void (*local_lpc_restore_signal_64bit)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); /* for use when the signal is <= 16 bits-per-sample, or <= 15 bits-per-sample on a side channel (which requires 1 extra bit): */ void (*local_lpc_restore_signal_16bit)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); - /* for use when the signal is <= 16 bits-per-sample, or <= 15 bits-per-sample on a side channel (which requires 1 extra bit), AND order <= 8: */ - void (*local_lpc_restore_signal_16bit_order8)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); - FLAC__bool (*local_bitreader_read_rice_signed_block)(FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter); void *client_data; FILE *file; /* only used if FLAC__stream_decoder_init_file()/FLAC__stream_decoder_init_file() called, else NULL */ FLAC__BitReader *input; @@ -380,7 +377,7 @@ static FLAC__StreamDecoderInitStatus init_stream_internal_( #if FLAC__HAS_OGG decoder->private_->is_ogg = is_ogg; if(is_ogg && !FLAC__ogg_decoder_aspect_init(&decoder->protected_->ogg_decoder_aspect)) - return decoder->protected_->state = FLAC__STREAM_DECODER_OGG_ERROR; + return decoder->protected_->initstate = FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE; #endif /* @@ -391,42 +388,44 @@ static FLAC__StreamDecoderInitStatus init_stream_internal_( decoder->private_->local_lpc_restore_signal = FLAC__lpc_restore_signal; decoder->private_->local_lpc_restore_signal_64bit = FLAC__lpc_restore_signal_wide; decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal; - decoder->private_->local_lpc_restore_signal_16bit_order8 = FLAC__lpc_restore_signal; - decoder->private_->local_bitreader_read_rice_signed_block = FLAC__bitreader_read_rice_signed_block; /* now override with asm where appropriate */ #ifndef FLAC__NO_ASM if(decoder->private_->cpuinfo.use_asm) { #ifdef FLAC__CPU_IA32 FLAC__ASSERT(decoder->private_->cpuinfo.type == FLAC__CPUINFO_TYPE_IA32); #ifdef FLAC__HAS_NASM -#if 1 /*@@@@@@ OPT: not clearly faster, needs more testing */ - if(decoder->private_->cpuinfo.data.ia32.bswap) - decoder->private_->local_bitreader_read_rice_signed_block = FLAC__bitreader_read_rice_signed_block_asm_ia32_bswap; -#endif - if(decoder->private_->cpuinfo.data.ia32.mmx) { + decoder->private_->local_lpc_restore_signal_64bit = FLAC__lpc_restore_signal_wide_asm_ia32; /* OPT_IA32: was really necessary for GCC < 4.9 */ + if(decoder->private_->cpuinfo.ia32.mmx) { decoder->private_->local_lpc_restore_signal = FLAC__lpc_restore_signal_asm_ia32; decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal_asm_ia32_mmx; - decoder->private_->local_lpc_restore_signal_16bit_order8 = FLAC__lpc_restore_signal_asm_ia32_mmx; } else { decoder->private_->local_lpc_restore_signal = FLAC__lpc_restore_signal_asm_ia32; decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal_asm_ia32; - decoder->private_->local_lpc_restore_signal_16bit_order8 = FLAC__lpc_restore_signal_asm_ia32; } #endif -#elif defined FLAC__CPU_PPC - FLAC__ASSERT(decoder->private_->cpuinfo.type == FLAC__CPUINFO_TYPE_PPC); - if(decoder->private_->cpuinfo.data.ppc.altivec) { - decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal_asm_ppc_altivec_16; - decoder->private_->local_lpc_restore_signal_16bit_order8 = FLAC__lpc_restore_signal_asm_ppc_altivec_16_order8; +#ifdef FLAC__HAS_X86INTRIN +# if defined FLAC__SSE2_SUPPORTED && !defined FLAC__HAS_NASM /* OPT_SSE: not better than MMX asm */ + if(decoder->private_->cpuinfo.ia32.sse2) { + decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal_16_intrin_sse2; + } +# endif +# if defined FLAC__SSE4_1_SUPPORTED + if(decoder->private_->cpuinfo.ia32.sse41) { + decoder->private_->local_lpc_restore_signal_64bit = FLAC__lpc_restore_signal_wide_intrin_sse41; } +# endif +#endif +#elif defined FLAC__CPU_X86_64 + FLAC__ASSERT(decoder->private_->cpuinfo.type == FLAC__CPUINFO_TYPE_X86_64); + /* No useful SSE optimizations yet */ #endif } #endif /* from here on, errors are fatal */ - if(!FLAC__bitreader_init(decoder->private_->input, decoder->private_->cpuinfo, read_callback_, decoder)) { + if(!FLAC__bitreader_init(decoder->private_->input, read_callback_, decoder)) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR; } @@ -528,10 +527,10 @@ static FLAC__StreamDecoderInitStatus init_FILE_internal_( FLAC__ASSERT(0 != file); if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) - return (FLAC__StreamDecoderInitStatus) (decoder->protected_->state = (FLAC__StreamDecoderState) FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED); + return decoder->protected_->initstate = FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED; if(0 == write_callback || 0 == error_callback) - return (FLAC__StreamDecoderInitStatus) (decoder->protected_->state = (FLAC__StreamDecoderState) FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS); + return decoder->protected_->initstate = FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS; /* * To make sure that our file does not go unclosed after an error, we @@ -602,10 +601,10 @@ static FLAC__StreamDecoderInitStatus init_file_internal_( * in FLAC__stream_decoder_init_FILE() before the FILE* is assigned. */ if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) - return (FLAC__StreamDecoderInitStatus) (decoder->protected_->state = (FLAC__StreamDecoderState) FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED); + return decoder->protected_->initstate = FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED; if(0 == write_callback || 0 == error_callback) - return (FLAC__StreamDecoderInitStatus) (decoder->protected_->state = (FLAC__StreamDecoderState) FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS); + return decoder->protected_->initstate = FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS; file = filename? flac_fopen(filename, "rb") : stdin; @@ -652,7 +651,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder) if(decoder->protected_->state == FLAC__STREAM_DECODER_UNINITIALIZED) return true; - /* see the comment in FLAC__seekable_stream_decoder_reset() as to why we + /* see the comment in FLAC__stream_decoder_reset() as to why we * always call FLAC__MD5Final() */ FLAC__MD5Final(decoder->private_->computed_md5sum, &decoder->private_->md5context); @@ -1316,9 +1315,6 @@ FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, unsigned size, unsigne memset(tmp, 0, sizeof(FLAC__int32)*4); decoder->private_->output[i] = tmp + 4; - /* WATCHOUT: - * minimum of quadword alignment for PPC vector optimizations is REQUIRED: - */ if(!FLAC__memory_alloc_aligned_int32_array(size, &decoder->private_->residual_unaligned[i], &decoder->private_->residual[i])) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return false; @@ -1368,6 +1364,10 @@ FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder) id_ = 0; continue; } + + if(id_ >= 3) + return false; + if(x == ID3V2_TAG_[id_]) { id_++; i = 0; @@ -1446,6 +1446,7 @@ FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder) unsigned real_length = length; FLAC__StreamMetadata block; + memset(&block, 0, sizeof(block)); block.is_last = is_last; block.type = (FLAC__MetadataType)type; block.length = length; @@ -1470,36 +1471,37 @@ FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder) return false; /* read_callback_ sets the state for us */ } else { + FLAC__bool ok = true; switch(type) { case FLAC__METADATA_TYPE_PADDING: /* skip the padding bytes */ if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, real_length)) - return false; /* read_callback_ sets the state for us */ + ok = false; /* read_callback_ sets the state for us */ break; case FLAC__METADATA_TYPE_APPLICATION: /* remember, we read the ID already */ if(real_length > 0) { if(0 == (block.data.application.data = (FLAC__byte*) malloc(real_length))) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; - return false; + ok = false; } - if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, block.data.application.data, real_length)) - return false; /* read_callback_ sets the state for us */ + else if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, block.data.application.data, real_length)) + ok = false; /* read_callback_ sets the state for us */ } else block.data.application.data = 0; break; case FLAC__METADATA_TYPE_VORBIS_COMMENT: - if(!read_metadata_vorbiscomment_(decoder, &block.data.vorbis_comment)) - return false; + if(!read_metadata_vorbiscomment_(decoder, &block.data.vorbis_comment, real_length)) + ok = false; break; case FLAC__METADATA_TYPE_CUESHEET: if(!read_metadata_cuesheet_(decoder, &block.data.cue_sheet)) - return false; + ok = false; break; case FLAC__METADATA_TYPE_PICTURE: if(!read_metadata_picture_(decoder, &block.data.picture)) - return false; + ok = false; break; case FLAC__METADATA_TYPE_STREAMINFO: case FLAC__METADATA_TYPE_SEEKTABLE: @@ -1509,16 +1511,16 @@ FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder) if(real_length > 0) { if(0 == (block.data.unknown.data = (FLAC__byte*) malloc(real_length))) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; - return false; + ok = false; } - if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, block.data.unknown.data, real_length)) - return false; /* read_callback_ sets the state for us */ + else if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, block.data.unknown.data, real_length)) + ok = false; /* read_callback_ sets the state for us */ } else block.data.unknown.data = 0; break; } - if(!decoder->private_->is_seeking && decoder->private_->metadata_callback) + if(ok && !decoder->private_->is_seeking && decoder->private_->metadata_callback) decoder->private_->metadata_callback(decoder, &block, decoder->private_->client_data); /* now we have to free any malloc()ed data in the block */ @@ -1563,6 +1565,9 @@ FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder) free(block.data.unknown.data); break; } + + if(!ok) /* anything that unsets "ok" should also make sure decoder->protected_->state is updated */ + return false; } } @@ -1689,58 +1694,88 @@ FLAC__bool read_metadata_seektable_(FLAC__StreamDecoder *decoder, FLAC__bool is_ return true; } -FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_VorbisComment *obj) +FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_VorbisComment *obj, unsigned length) { FLAC__uint32 i; FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); /* read vendor string */ - FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN == 32); - if(!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->vendor_string.length)) - return false; /* read_callback_ sets the state for us */ - if(obj->vendor_string.length > 0) { - if(0 == (obj->vendor_string.entry = (FLAC__byte*) safe_malloc_add_2op_(obj->vendor_string.length, /*+*/1))) { - decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; - return false; - } - if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->vendor_string.entry, obj->vendor_string.length)) + if (length >= 8) { + length -= 8; /* vendor string length + num comments entries alone take 8 bytes */ + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN == 32); + if (!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->vendor_string.length)) return false; /* read_callback_ sets the state for us */ - obj->vendor_string.entry[obj->vendor_string.length] = '\0'; - } - else - obj->vendor_string.entry = 0; + if (obj->vendor_string.length > 0) { + if (length < obj->vendor_string.length) { + obj->vendor_string.length = 0; + obj->vendor_string.entry = 0; + goto skip; + } + else + length -= obj->vendor_string.length; + if (0 == (obj->vendor_string.entry = (FLAC__byte*) safe_malloc_add_2op_(obj->vendor_string.length, /*+*/1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if (!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->vendor_string.entry, obj->vendor_string.length)) + return false; /* read_callback_ sets the state for us */ + obj->vendor_string.entry[obj->vendor_string.length] = '\0'; + } + else + obj->vendor_string.entry = 0; - /* read num comments */ - FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN == 32); - if(!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->num_comments)) - return false; /* read_callback_ sets the state for us */ + /* read num comments */ + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN == 32); + if (!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->num_comments)) + return false; /* read_callback_ sets the state for us */ - /* read comments */ - if(obj->num_comments > 0) { - if(0 == (obj->comments = (FLAC__StreamMetadata_VorbisComment_Entry*) safe_malloc_mul_2op_p(obj->num_comments, /*times*/sizeof(FLAC__StreamMetadata_VorbisComment_Entry)))) { - decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; - return false; - } - for(i = 0; i < obj->num_comments; i++) { - FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN == 32); - if(!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->comments[i].length)) - return false; /* read_callback_ sets the state for us */ - if(obj->comments[i].length > 0) { - if(0 == (obj->comments[i].entry = (FLAC__byte*) safe_malloc_add_2op_(obj->comments[i].length, /*+*/1))) { - decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; - return false; + /* read comments */ + if (obj->num_comments > 0) { + if (0 == (obj->comments = (FLAC__StreamMetadata_VorbisComment_Entry*) safe_malloc_mul_2op_p(obj->num_comments, /*times*/sizeof(FLAC__StreamMetadata_VorbisComment_Entry)))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + for (i = 0; i < obj->num_comments; i++) { + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN == 32); + if (length < 4) { + obj->num_comments = i; + goto skip; } - if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->comments[i].entry, obj->comments[i].length)) + else + length -= 4; + if (!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->comments[i].length)) return false; /* read_callback_ sets the state for us */ - obj->comments[i].entry[obj->comments[i].length] = '\0'; + if (obj->comments[i].length > 0) { + if (length < obj->comments[i].length) { + obj->comments[i].length = 0; + obj->comments[i].entry = 0; + obj->num_comments = i; + goto skip; + } + else + length -= obj->comments[i].length; + if (0 == (obj->comments[i].entry = (FLAC__byte*) safe_malloc_add_2op_(obj->comments[i].length, /*+*/1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if (!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->comments[i].entry, obj->comments[i].length)) + return false; /* read_callback_ sets the state for us */ + obj->comments[i].entry[obj->comments[i].length] = '\0'; + } + else + obj->comments[i].entry = 0; } - else - obj->comments[i].entry = 0; } + else + obj->comments = 0; } - else { - obj->comments = 0; + + skip: + if (length > 0) { + /* This will only happen on files with invalid data in comments */ + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, length)) + return false; /* read_callback_ sets the state for us */ } return true; @@ -2655,12 +2690,8 @@ FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, unsigned channel, un if( (FLAC__uint64)order * ((((FLAC__uint64)1)<qlp_coeff_precision)-1) < (((FLAC__uint64)-1) << 32) ) */ if(bps + subframe->qlp_coeff_precision + FLAC__bitmath_ilog2(order) <= 32) - if(bps <= 16 && subframe->qlp_coeff_precision <= 16) { - if(order <= 8) - decoder->private_->local_lpc_restore_signal_16bit_order8(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); - else - decoder->private_->local_lpc_restore_signal_16bit(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); - } + if(bps <= 16 && subframe->qlp_coeff_precision <= 16) + decoder->private_->local_lpc_restore_signal_16bit(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); else decoder->private_->local_lpc_restore_signal(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); else @@ -2708,14 +2739,16 @@ FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, unsigne if(decoder->private_->frame.header.blocksize < predictor_order) { send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; - return true; + /* We have received a potentially malicious bit stream. All we can do is error out to avoid a heap overflow. */ + return false; } } else { if(partition_samples < predictor_order) { send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; - return true; + /* We have received a potentially malicious bit stream. All we can do is error out to avoid a heap overflow. */ + return false; } } @@ -2732,7 +2765,7 @@ FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, unsigne if(rice_parameter < pesc) { partitioned_rice_contents->raw_bits[partition] = 0; u = (partition_order == 0 || partition > 0)? partition_samples : partition_samples - predictor_order; - if(!decoder->private_->local_bitreader_read_rice_signed_block(decoder->private_->input, residual + sample, u, rice_parameter)) + if(!FLAC__bitreader_read_rice_signed_block(decoder->private_->input, residual + sample, u, rice_parameter)) return false; /* read_callback_ sets the state for us */ sample += u; } @@ -3074,12 +3107,7 @@ FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 s return false; } #ifndef FLAC__INTEGER_ONLY_LIBRARY -#if defined _MSC_VER || defined __MINGW32__ - /* with VC++ you have to spoon feed it the casting */ - pos = (FLAC__int64)lower_bound + (FLAC__int64)((FLAC__double)(FLAC__int64)(target_sample - lower_bound_sample) / (FLAC__double)(FLAC__int64)(upper_bound_sample - lower_bound_sample) * (FLAC__double)(FLAC__int64)(upper_bound - lower_bound)) - approx_bytes_per_frame; -#else pos = (FLAC__int64)lower_bound + (FLAC__int64)((FLAC__double)(target_sample - lower_bound_sample) / (FLAC__double)(upper_bound_sample - lower_bound_sample) * (FLAC__double)(upper_bound - lower_bound)) - approx_bytes_per_frame; -#endif #else /* a little less accurate: */ if(upper_bound - lower_bound < 0xffffffff) @@ -3203,12 +3231,7 @@ FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint } else { #ifndef FLAC__INTEGER_ONLY_LIBRARY -#if defined _MSC_VER || defined __MINGW32__ - /* with MSVC you have to spoon feed it the casting */ - pos = (FLAC__uint64)((FLAC__double)(FLAC__int64)(target_sample - left_sample) / (FLAC__double)(FLAC__int64)(right_sample - left_sample) * (FLAC__double)(FLAC__int64)(right_pos - left_pos)); -#else pos = (FLAC__uint64)((FLAC__double)(target_sample - left_sample) / (FLAC__double)(right_sample - left_sample) * (FLAC__double)(right_pos - left_pos)); -#endif #else /* a little less accurate: */ if ((target_sample-left_sample <= 0xffffffff) && (right_pos-left_pos <= 0xffffffff)) diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder.c b/source/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder.c index 20e98dacf..f5eb90e85 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder.c +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,7 +30,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H # include #endif @@ -55,10 +55,10 @@ #include "include/private/ogg_helper.h" #include "include/private/ogg_mapping.h" #endif +#include "include/private/stream_encoder.h" #include "include/private/stream_encoder_framing.h" #include "include/private/window.h" #include "../alloc.h" -#include "../compat.h" /* Exact Rice codeword length calculation is off by default. The simple @@ -103,16 +103,18 @@ static struct CompressionLevels { unsigned min_residual_partition_order; unsigned max_residual_partition_order; unsigned rice_parameter_search_dist; + const char *apodization; } compression_levels_[] = { - { false, false, 0, 0, false, false, false, 0, 3, 0 }, - { true , true , 0, 0, false, false, false, 0, 3, 0 }, - { true , false, 0, 0, false, false, false, 0, 3, 0 }, - { false, false, 6, 0, false, false, false, 0, 4, 0 }, - { true , true , 8, 0, false, false, false, 0, 4, 0 }, - { true , false, 8, 0, false, false, false, 0, 5, 0 }, - { true , false, 8, 0, false, false, false, 0, 6, 0 }, - { true , false, 8, 0, false, false, true , 0, 6, 0 }, - { true , false, 12, 0, false, false, true , 0, 6, 0 } + { false, false, 0, 0, false, false, false, 0, 3, 0, "tukey(5e-1)" }, + { true , true , 0, 0, false, false, false, 0, 3, 0, "tukey(5e-1)" }, + { true , false, 0, 0, false, false, false, 0, 3, 0, "tukey(5e-1)" }, + { false, false, 6, 0, false, false, false, 0, 4, 0, "tukey(5e-1)" }, + { true , true , 8, 0, false, false, false, 0, 4, 0, "tukey(5e-1)" }, + { true , false, 8, 0, false, false, false, 0, 5, 0, "tukey(5e-1)" }, + { true , false, 8, 0, false, false, false, 0, 6, 0, "tukey(5e-1);partial_tukey(2)" }, + { true , false, 12, 0, false, false, false, 0, 6, 0, "tukey(5e-1);partial_tukey(2)" }, + { true , false, 12, 0, false, false, false, 0, 6, 0, "tukey(5e-1);partial_tukey(2);punchout_tukey(3)" } + /* here we use locale-independent 5e-1 instead of 0.5 or 0,5 */ }; @@ -342,10 +344,13 @@ typedef struct FLAC__StreamEncoderPrivate { unsigned current_frame_number; FLAC__MD5Context md5context; FLAC__CPUInfo cpuinfo; + void (*local_precompute_partition_info_sums)(const FLAC__int32 residual[], FLAC__uint64 abs_residual_partition_sums[], unsigned residual_samples, unsigned predictor_order, unsigned min_partition_order, unsigned max_partition_order, unsigned bps); #ifndef FLAC__INTEGER_ONLY_LIBRARY unsigned (*local_fixed_compute_best_predictor)(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); + unsigned (*local_fixed_compute_best_predictor_wide)(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); #else unsigned (*local_fixed_compute_best_predictor)(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); + unsigned (*local_fixed_compute_best_predictor_wide)(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); #endif #ifndef FLAC__INTEGER_ONLY_LIBRARY void (*local_lpc_compute_autocorrelation)(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); @@ -873,7 +878,9 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_( #ifndef FLAC__INTEGER_ONLY_LIBRARY encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation; #endif + encoder->private_->local_precompute_partition_info_sums = precompute_partition_info_sums_; encoder->private_->local_fixed_compute_best_predictor = FLAC__fixed_compute_best_predictor; + encoder->private_->local_fixed_compute_best_predictor_wide = FLAC__fixed_compute_best_predictor_wide; #ifndef FLAC__INTEGER_ONLY_LIBRARY encoder->private_->local_lpc_compute_residual_from_qlp_coefficients = FLAC__lpc_compute_residual_from_qlp_coefficients; encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_64bit = FLAC__lpc_compute_residual_from_qlp_coefficients_wide; @@ -886,21 +893,23 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_( # ifdef FLAC__CPU_IA32 FLAC__ASSERT(encoder->private_->cpuinfo.type == FLAC__CPUINFO_TYPE_IA32); # ifdef FLAC__HAS_NASM - if(encoder->private_->cpuinfo.data.ia32.sse) { + if(encoder->private_->cpuinfo.ia32.sse) { if(encoder->protected_->max_lpc_order < 4) encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_4; else if(encoder->protected_->max_lpc_order < 8) encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_8; else if(encoder->protected_->max_lpc_order < 12) encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_12; + else if(encoder->protected_->max_lpc_order < 16) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_16; else encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32; } - else if(encoder->private_->cpuinfo.data.ia32._3dnow) - encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32_3dnow; else encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32; - if(encoder->private_->cpuinfo.data.ia32.mmx) { + + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_64bit = FLAC__lpc_compute_residual_from_qlp_coefficients_wide_asm_ia32; /* OPT_IA32: was really necessary for GCC < 4.9 */ + if(encoder->private_->cpuinfo.ia32.mmx) { encoder->private_->local_lpc_compute_residual_from_qlp_coefficients = FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32; encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit = FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx; } @@ -908,16 +917,137 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_( encoder->private_->local_lpc_compute_residual_from_qlp_coefficients = FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32; encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit = FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32; } - if(encoder->private_->cpuinfo.data.ia32.mmx && encoder->private_->cpuinfo.data.ia32.cmov) + + if(encoder->private_->cpuinfo.ia32.mmx && encoder->private_->cpuinfo.ia32.cmov) encoder->private_->local_fixed_compute_best_predictor = FLAC__fixed_compute_best_predictor_asm_ia32_mmx_cmov; # endif /* FLAC__HAS_NASM */ -# endif /* FLAC__CPU_IA32 */ +# ifdef FLAC__HAS_X86INTRIN +# if defined FLAC__SSE_SUPPORTED + if(encoder->private_->cpuinfo.ia32.sse) { + if(encoder->protected_->max_lpc_order < 4) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse_lag_4; + else if(encoder->protected_->max_lpc_order < 8) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse_lag_8; + else if(encoder->protected_->max_lpc_order < 12) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse_lag_12; + else if(encoder->protected_->max_lpc_order < 16) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse_lag_16; + else + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation; + } +# endif + +# ifdef FLAC__SSE2_SUPPORTED + if(encoder->private_->cpuinfo.ia32.sse2) { + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients = FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_sse2; + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit = FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_sse2; + } +# endif +# ifdef FLAC__SSE4_1_SUPPORTED + if(encoder->private_->cpuinfo.ia32.sse41) { + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients = FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_sse41; + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_64bit = FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_sse41; + } +# endif +# ifdef FLAC__AVX2_SUPPORTED + if(encoder->private_->cpuinfo.ia32.avx2) { + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit = FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_avx2; + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients = FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_avx2; + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_64bit = FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_avx2; + } +# endif + +# ifdef FLAC__SSE2_SUPPORTED + if (encoder->private_->cpuinfo.ia32.sse2) { + encoder->private_->local_fixed_compute_best_predictor = FLAC__fixed_compute_best_predictor_intrin_sse2; + encoder->private_->local_fixed_compute_best_predictor_wide = FLAC__fixed_compute_best_predictor_wide_intrin_sse2; + } +# endif +# ifdef FLAC__SSSE3_SUPPORTED + if (encoder->private_->cpuinfo.ia32.ssse3) { + encoder->private_->local_fixed_compute_best_predictor = FLAC__fixed_compute_best_predictor_intrin_ssse3; + encoder->private_->local_fixed_compute_best_predictor_wide = FLAC__fixed_compute_best_predictor_wide_intrin_ssse3; + } +# endif +# endif /* FLAC__HAS_X86INTRIN */ +# elif defined FLAC__CPU_X86_64 + FLAC__ASSERT(encoder->private_->cpuinfo.type == FLAC__CPUINFO_TYPE_X86_64); +# ifdef FLAC__HAS_X86INTRIN +# ifdef FLAC__SSE_SUPPORTED + if(encoder->protected_->max_lpc_order < 4) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse_lag_4; + else if(encoder->protected_->max_lpc_order < 8) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse_lag_8; + else if(encoder->protected_->max_lpc_order < 12) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse_lag_12; + else if(encoder->protected_->max_lpc_order < 16) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse_lag_16; +# endif + +# ifdef FLAC__SSE2_SUPPORTED + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit = FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_sse2; +# endif +# ifdef FLAC__SSE4_1_SUPPORTED + if(encoder->private_->cpuinfo.x86.sse41) { + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients = FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_sse41; + } +# endif +# ifdef FLAC__AVX2_SUPPORTED + if(encoder->private_->cpuinfo.x86.avx2) { + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit = FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_avx2; + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients = FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_avx2; + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_64bit = FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_avx2; + } +# endif + +# ifdef FLAC__SSE2_SUPPORTED + encoder->private_->local_fixed_compute_best_predictor = FLAC__fixed_compute_best_predictor_intrin_sse2; + encoder->private_->local_fixed_compute_best_predictor_wide = FLAC__fixed_compute_best_predictor_wide_intrin_sse2; +# endif +# ifdef FLAC__SSSE3_SUPPORTED + if (encoder->private_->cpuinfo.x86.ssse3) { + encoder->private_->local_fixed_compute_best_predictor = FLAC__fixed_compute_best_predictor_intrin_ssse3; + encoder->private_->local_fixed_compute_best_predictor_wide = FLAC__fixed_compute_best_predictor_wide_intrin_ssse3; + } +# endif +# endif /* FLAC__HAS_X86INTRIN */ +# endif /* FLAC__CPU_... */ } # endif /* !FLAC__NO_ASM */ #endif /* !FLAC__INTEGER_ONLY_LIBRARY */ +#if !defined FLAC__NO_ASM && defined FLAC__HAS_X86INTRIN + if(encoder->private_->cpuinfo.use_asm) { +# if defined FLAC__CPU_IA32 +# ifdef FLAC__SSE2_SUPPORTED + if(encoder->private_->cpuinfo.ia32.sse2) + encoder->private_->local_precompute_partition_info_sums = FLAC__precompute_partition_info_sums_intrin_sse2; +# endif +# ifdef FLAC__SSSE3_SUPPORTED + if(encoder->private_->cpuinfo.ia32.ssse3) + encoder->private_->local_precompute_partition_info_sums = FLAC__precompute_partition_info_sums_intrin_ssse3; +# endif +# ifdef FLAC__AVX2_SUPPORTED + if(encoder->private_->cpuinfo.ia32.avx2) + encoder->private_->local_precompute_partition_info_sums = FLAC__precompute_partition_info_sums_intrin_avx2; +# endif +# elif defined FLAC__CPU_X86_64 +# ifdef FLAC__SSE2_SUPPORTED + encoder->private_->local_precompute_partition_info_sums = FLAC__precompute_partition_info_sums_intrin_sse2; +# endif +# ifdef FLAC__SSSE3_SUPPORTED + if(encoder->private_->cpuinfo.x86.ssse3) + encoder->private_->local_precompute_partition_info_sums = FLAC__precompute_partition_info_sums_intrin_ssse3; +# endif +# ifdef FLAC__AVX2_SUPPORTED + if(encoder->private_->cpuinfo.x86.avx2) + encoder->private_->local_precompute_partition_info_sums = FLAC__precompute_partition_info_sums_intrin_avx2; +# endif +# endif /* FLAC__CPU_... */ + } +#endif /* !FLAC__NO_ASM && FLAC__HAS_X86INTRIN */ /* finally override based on wide-ness if necessary */ if(encoder->private_->use_wide_by_block) { - encoder->private_->local_fixed_compute_best_predictor = FLAC__fixed_compute_best_predictor_wide; + encoder->private_->local_fixed_compute_best_predictor = encoder->private_->local_fixed_compute_best_predictor_wide; } /* set state to OK; from here on, errors are fatal and we'll override the state then */ @@ -1152,7 +1282,7 @@ static FLAC__StreamEncoderInitStatus init_FILE_internal_( FLAC__StreamEncoder *encoder, FILE *file, FLAC__StreamEncoderProgressCallback progress_callback, - void * /*client_data*/, + void *client_data, FLAC__bool is_ogg ) { @@ -1178,6 +1308,13 @@ static FLAC__StreamEncoderInitStatus init_FILE_internal_( if(file == stdout) file = get_binary_stdout_(); /* just to be safe */ +#ifdef _WIN32 + /* + * Windows can suffer quite badly from disk fragmentation. This can be + * reduced significantly by setting the output buffer size to be 10MB. + */ + setvbuf(file, NULL, _IOFBF, 10*1024*1024); +#endif encoder->private_->file = file; encoder->private_->progress_callback = progress_callback; @@ -1447,11 +1584,10 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_compression_level(FLAC__StreamEncod ok &= FLAC__stream_encoder_set_do_mid_side_stereo (encoder, compression_levels_[value].do_mid_side_stereo); ok &= FLAC__stream_encoder_set_loose_mid_side_stereo (encoder, compression_levels_[value].loose_mid_side_stereo); #ifndef FLAC__INTEGER_ONLY_LIBRARY -#if 0 - /* was: */ +#if 1 ok &= FLAC__stream_encoder_set_apodization (encoder, compression_levels_[value].apodization); - /* but it's too hard to specify the string in a locale-specific way */ #else + /* equivalent to -A tukey(0.5) */ encoder->protected_->num_apodizations = 1; encoder->protected_->apodizations[0].type = FLAC__APODIZATION_TUKEY; encoder->protected_->apodizations[0].parameters.tukey.p = 0.5; @@ -1555,6 +1691,48 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_apodization(FLAC__StreamEncoder *en encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_TUKEY; } } + else if(n>15 && 0 == strncmp("partial_tukey(" , specification, 14)) { + FLAC__int32 tukey_parts = (FLAC__int32)strtod(specification+14, 0); + const char *si_1 = strchr(specification, '/'); + FLAC__real overlap = si_1?flac_min((FLAC__real)strtod(si_1+1, 0),0.99f):0.1f; + FLAC__real overlap_units = 1.0f/(1.0f - overlap) - 1.0f; + const char *si_2 = strchr((si_1?(si_1+1):specification), '/'); + FLAC__real tukey_p = si_2?(FLAC__real)strtod(si_2+1, 0):0.2f; + + if (tukey_parts <= 1) { + encoder->protected_->apodizations[encoder->protected_->num_apodizations].parameters.tukey.p = tukey_p; + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_TUKEY; + }else if (encoder->protected_->num_apodizations + tukey_parts < 32){ + FLAC__int32 m; + for(m = 0; m < tukey_parts; m++){ + encoder->protected_->apodizations[encoder->protected_->num_apodizations].parameters.multiple_tukey.p = tukey_p; + encoder->protected_->apodizations[encoder->protected_->num_apodizations].parameters.multiple_tukey.start = m/(tukey_parts+overlap_units); + encoder->protected_->apodizations[encoder->protected_->num_apodizations].parameters.multiple_tukey.end = (m+1+overlap_units)/(tukey_parts+overlap_units); + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_PARTIAL_TUKEY; + } + } + } + else if(n>16 && 0 == strncmp("punchout_tukey(" , specification, 15)) { + FLAC__int32 tukey_parts = (FLAC__int32)strtod(specification+15, 0); + const char *si_1 = strchr(specification, '/'); + FLAC__real overlap = si_1?flac_min((FLAC__real)strtod(si_1+1, 0),0.99f):0.2f; + FLAC__real overlap_units = 1.0f/(1.0f - overlap) - 1.0f; + const char *si_2 = strchr((si_1?(si_1+1):specification), '/'); + FLAC__real tukey_p = si_2?(FLAC__real)strtod(si_2+1, 0):0.2f; + + if (tukey_parts <= 1) { + encoder->protected_->apodizations[encoder->protected_->num_apodizations].parameters.tukey.p = tukey_p; + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_TUKEY; + }else if (encoder->protected_->num_apodizations + tukey_parts < 32){ + FLAC__int32 m; + for(m = 0; m < tukey_parts; m++){ + encoder->protected_->apodizations[encoder->protected_->num_apodizations].parameters.multiple_tukey.p = tukey_p; + encoder->protected_->apodizations[encoder->protected_->num_apodizations].parameters.multiple_tukey.start = m/(tukey_parts+overlap_units); + encoder->protected_->apodizations[encoder->protected_->num_apodizations].parameters.multiple_tukey.end = (m+1+overlap_units)/(tukey_parts+overlap_units); + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_PUNCHOUT_TUKEY; + } + } + } else if(n==5 && 0 == strncmp("welch" , specification, n)) encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_WELCH; if (encoder->protected_->num_apodizations == 32) @@ -2236,8 +2414,8 @@ FLAC__bool resize_buffers_(FLAC__StreamEncoder *encoder, unsigned new_blocksize) ok = true; - /* WATCHOUT: FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx() - * requires that the input arrays (in our case the integer signals) + /* WATCHOUT: FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx() and ..._intrin_sse2() + * require that the input arrays (in our case the integer signals) * have a buffer of up to 3 zeroes in front (at negative indices) for * alignment purposes; we use 4 in front to keep the data well-aligned. */ @@ -2334,6 +2512,12 @@ FLAC__bool resize_buffers_(FLAC__StreamEncoder *encoder, unsigned new_blocksize) case FLAC__APODIZATION_TUKEY: FLAC__window_tukey(encoder->private_->window[i], new_blocksize, encoder->protected_->apodizations[i].parameters.tukey.p); break; + case FLAC__APODIZATION_PARTIAL_TUKEY: + FLAC__window_partial_tukey(encoder->private_->window[i], new_blocksize, encoder->protected_->apodizations[i].parameters.multiple_tukey.p, encoder->protected_->apodizations[i].parameters.multiple_tukey.start, encoder->protected_->apodizations[i].parameters.multiple_tukey.end); + break; + case FLAC__APODIZATION_PUNCHOUT_TUKEY: + FLAC__window_punchout_tukey(encoder->private_->window[i], new_blocksize, encoder->protected_->apodizations[i].parameters.multiple_tukey.p, encoder->protected_->apodizations[i].parameters.multiple_tukey.start, encoder->protected_->apodizations[i].parameters.multiple_tukey.end); + break; case FLAC__APODIZATION_WELCH: FLAC__window_welch(encoder->private_->window[i], new_blocksize); break; @@ -3165,7 +3349,7 @@ FLAC__bool process_subframe_( #endif #ifndef FLAC__INTEGER_ONLY_LIBRARY FLAC__double lpc_residual_bits_per_sample; - FLAC__real autoc[FLAC__MAX_LPC_ORDER+1]; /* WATCHOUT: the size is important even though encoder->protected_->max_lpc_order might be less; some asm routines need all the space */ + FLAC__real autoc[FLAC__MAX_LPC_ORDER+1]; /* WATCHOUT: the size is important even though encoder->protected_->max_lpc_order might be less; some asm and x86 intrinsic routines need all the space */ FLAC__double lpc_error[FLAC__MAX_LPC_ORDER]; unsigned min_lpc_order, max_lpc_order, lpc_order; unsigned min_qlp_coeff_precision, max_qlp_coeff_precision, qlp_coeff_precision; @@ -3318,9 +3502,9 @@ FLAC__bool process_subframe_( } if(encoder->protected_->do_qlp_coeff_prec_search) { min_qlp_coeff_precision = FLAC__MIN_QLP_COEFF_PRECISION; - /* try to ensure a 32-bit datapath throughout for 16bps(+1bps for side channel) or less */ - if(subframe_bps <= 17) { - max_qlp_coeff_precision = flac_min(32 - subframe_bps - lpc_order, FLAC__MAX_QLP_COEFF_PRECISION); + /* try to keep qlp coeff precision such that only 32-bit math is required for decode of <=16bps streams */ + if(subframe_bps <= 16) { + max_qlp_coeff_precision = flac_min(32 - subframe_bps - FLAC__bitmath_ilog2(lpc_order), FLAC__MAX_QLP_COEFF_PRECISION); max_qlp_coeff_precision = flac_max(max_qlp_coeff_precision, min_qlp_coeff_precision); } else @@ -3556,7 +3740,7 @@ unsigned evaluate_lpc_subframe_( FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents ) { - FLAC__int32 qlp_coeff[FLAC__MAX_LPC_ORDER]; + FLAC__int32 qlp_coeff[FLAC__MAX_LPC_ORDER]; /* WATCHOUT: the size is important; some x86 intrinsic routines need more than lpc order elements */ unsigned i, residual_bits, estimate; int quantization, ret; const unsigned residual_samples = blocksize - order; @@ -3671,7 +3855,7 @@ unsigned find_best_partition_order_( max_partition_order = FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order(max_partition_order, blocksize, predictor_order); min_partition_order = flac_min(min_partition_order, max_partition_order); - precompute_partition_info_sums_(residual, abs_residual_partition_sums, residual_samples, predictor_order, min_partition_order, max_partition_order, bps); + private_->local_precompute_partition_info_sums(residual, abs_residual_partition_sums, residual_samples, predictor_order, min_partition_order, max_partition_order, bps); if(do_escape_coding) precompute_partition_info_escapes_(residual, raw_bits_per_partition, residual_samples, predictor_order, min_partition_order, max_partition_order); @@ -3743,17 +3927,6 @@ unsigned find_best_partition_order_( return best_residual_bits; } -#if defined(FLAC__CPU_IA32) && !defined FLAC__NO_ASM && defined FLAC__HAS_NASM -extern void precompute_partition_info_sums_32bit_asm_ia32_( - const FLAC__int32 residual[], - FLAC__uint64 abs_residual_partition_sums[], - unsigned blocksize, - unsigned predictor_order, - unsigned min_partition_order, - unsigned max_partition_order -); -#endif - void precompute_partition_info_sums_( const FLAC__int32 residual[], FLAC__uint64 abs_residual_partition_sums[], @@ -3769,21 +3942,12 @@ void precompute_partition_info_sums_( FLAC__ASSERT(default_partition_samples > predictor_order); -#if defined(FLAC__CPU_IA32) && !defined FLAC__NO_ASM && defined FLAC__HAS_NASM - /* slightly pessimistic but still catches all common cases */ - /* WATCHOUT: "+ bps" is an assumption that the average residual magnitude will not be more than "bps" bits */ - if(FLAC__bitmath_ilog2(default_partition_samples) + bps < 32) { - precompute_partition_info_sums_32bit_asm_ia32_(residual, abs_residual_partition_sums, residual_samples + predictor_order, predictor_order, min_partition_order, max_partition_order); - return; - } -#endif - /* first do max_partition_order */ { unsigned partition, residual_sample, end = (unsigned)(-(int)predictor_order); - /* slightly pessimistic but still catches all common cases */ - /* WATCHOUT: "+ bps" is an assumption that the average residual magnitude will not be more than "bps" bits */ - if(FLAC__bitmath_ilog2(default_partition_samples) + bps < 32) { + /* WATCHOUT: "+ bps + FLAC__MAX_EXTRA_RESIDUAL_BPS" is the maximum + * assumed size of the average residual magnitude */ + if(FLAC__bitmath_ilog2(default_partition_samples) + bps + FLAC__MAX_EXTRA_RESIDUAL_BPS < 32) { FLAC__uint32 abs_residual_partition_sum; for(partition = residual_sample = 0; partition < partitions; partition++) { @@ -4027,8 +4191,35 @@ FLAC__bool set_partitioned_rice_( * in the partition, so the actual mean is * mean/partition_samples */ +#if 0 /* old simple code */ for(rice_parameter = 0, k = partition_samples; k < mean; rice_parameter++, k <<= 1) ; +#else +#if defined FLAC__CPU_X86_64 /* and other 64-bit arch, too */ + if(mean <= 0x80000000/512) { /* 512: more or less optimal for both 16- and 24-bit input */ +#else + if(mean <= 0x80000000/8) { /* 32-bit arch: use 32-bit math if possible */ +#endif + FLAC__uint32 k2, mean2 = (FLAC__uint32) mean; + rice_parameter = 0; k2 = partition_samples; + while(k2*8 < mean2) { /* requires: mean <= (2^31)/8 */ + rice_parameter += 4; k2 <<= 4; /* tuned for 16-bit input */ + } + while(k2 < mean2) { /* requires: mean <= 2^31 */ + rice_parameter++; k2 <<= 1; + } + } + else { + rice_parameter = 0; k = partition_samples; + if(mean <= FLAC__U64L(0x8000000000000000)/128) /* usually mean is _much_ smaller than this value */ + while(k*128 < mean) { /* requires: mean <= (2^63)/128 */ + rice_parameter += 8; k <<= 8; /* tuned for 24-bit input */ + } + while(k < mean) { /* requires: mean <= 2^63 */ + rice_parameter++; k <<= 1; + } + } +#endif if(rice_parameter >= rice_parameter_limit) { #ifdef DEBUG_VERBOSE fprintf(stderr, "clipping rice_parameter (%u -> %u) @6\n", rice_parameter, rice_parameter_limit - 1); diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder_framing.c b/source/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder_framing.c index 41efca5ef..6d57006ad 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder_framing.c +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder_framing.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,7 +30,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H # include #endif diff --git a/source/modules/juce_audio_formats/codecs/flac/libFLAC/window_flac.c b/source/modules/juce_audio_formats/codecs/flac/libFLAC/window_flac.c index c99d5b4ef..dc341a67f 100644 --- a/source/modules/juce_audio_formats/codecs/flac/libFLAC/window_flac.c +++ b/source/modules/juce_audio_formats/codecs/flac/libFLAC/window_flac.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2006-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,7 +30,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H # include #endif @@ -41,11 +41,6 @@ #ifndef FLAC__INTEGER_ONLY_LIBRARY -#ifndef M_PI -/* math.h in VC++ doesn't seem to have this (how Microsoft is that?) */ -#define M_PI 3.14159265358979323846 -#endif - void FLAC__window_bartlett(FLAC__real *window, const FLAC__int32 L) { @@ -62,7 +57,7 @@ void FLAC__window_bartlett(FLAC__real *window, const FLAC__int32 L) for (n = 0; n <= L/2-1; n++) window[n] = 2.0f * n / (float)N; for (; n <= N; n++) - window[n] = 2.0f - 2.0f * (N-n) / (float)N; + window[n] = 2.0f - 2.0f * n / (float)N; } } @@ -72,7 +67,7 @@ void FLAC__window_bartlett_hann(FLAC__real *window, const FLAC__int32 L) FLAC__int32 n; for (n = 0; n < L; n++) - window[n] = (FLAC__real)(0.62f - 0.48f * fabs((float)n/(float)N+0.5f) + 0.38f * cos(2.0f * M_PI * ((float)n/(float)N+0.5f))); + window[n] = (FLAC__real)(0.62f - 0.48f * fabs((float)n/(float)N-0.5f) - 0.38f * cos(2.0f * M_PI * ((float)n/(float)N))); } void FLAC__window_blackman(FLAC__real *window, const FLAC__int32 L) @@ -177,16 +172,16 @@ void FLAC__window_triangle(FLAC__real *window, const FLAC__int32 L) FLAC__int32 n; if (L & 1) { - for (n = 1; n <= L+1/2; n++) + for (n = 1; n <= (L+1)/2; n++) window[n-1] = 2.0f * n / ((float)L + 1.0f); for (; n <= L; n++) - window[n-1] = - (float)(2 * (L - n + 1)) / ((float)L + 1.0f); + window[n-1] = (float)(2 * (L - n + 1)) / ((float)L + 1.0f); } else { for (n = 1; n <= L/2; n++) - window[n-1] = 2.0f * n / (float)L; + window[n-1] = 2.0f * n / ((float)L + 1.0f); for (; n <= L; n++) - window[n-1] = ((float)(2 * (L - n)) + 1.0f) / (float)L; + window[n-1] = (float)(2 * (L - n + 1)) / ((float)L + 1.0f); } } @@ -211,6 +206,66 @@ void FLAC__window_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__rea } } +void FLAC__window_partial_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p, const FLAC__real start, const FLAC__real end) +{ + const FLAC__int32 start_n = (FLAC__int32)(start * L); + const FLAC__int32 end_n = (FLAC__int32)(end * L); + const FLAC__int32 N = end_n - start_n; + FLAC__int32 Np, n, i; + + if (p <= 0.0f) + FLAC__window_partial_tukey(window, L, 0.05f, start, end); + else if (p >= 1.0f) + FLAC__window_partial_tukey(window, L, 0.95f, start, end); + else { + + Np = (FLAC__int32)(p / 2.0f * N); + + for (n = 0; n < start_n && n < L; n++) + window[n] = 0.0f; + for (i = 1; n < (start_n+Np) && n < L; n++, i++) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Np)); + for (; n < (end_n-Np) && n < L; n++) + window[n] = 1.0f; + for (i = Np; n < end_n && n < L; n++, i--) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Np)); + for (; n < L; n++) + window[n] = 0.0f; + } +} + +void FLAC__window_punchout_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p, const FLAC__real start, const FLAC__real end) +{ + const FLAC__int32 start_n = (FLAC__int32)(start * L); + const FLAC__int32 end_n = (FLAC__int32)(end * L); + FLAC__int32 Ns, Ne, n, i; + + if (p <= 0.0f) + FLAC__window_punchout_tukey(window, L, 0.05f, start, end); + else if (p >= 1.0f) + FLAC__window_punchout_tukey(window, L, 0.95f, start, end); + else { + + Ns = (FLAC__int32)(p / 2.0f * start_n); + Ne = (FLAC__int32)(p / 2.0f * (L - end_n)); + + for (n = 0, i = 1; n < Ns && n < L; n++, i++) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Ns)); + for (; n < start_n-Ns && n < L; n++) + window[n] = 1.0f; + for (i = Ns; n < start_n && n < L; n++, i--) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Ns)); + for (; n < end_n && n < L; n++) + window[n] = 0.0f; + for (i = 1; n < end_n+Ne && n < L; n++, i++) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Ne)); + for (; n < L - (Ne) && n < L; n++) + window[n] = 1.0f; + for (i = Ne; n < L; n++, i--) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Ne)); + } +} + void FLAC__window_welch(FLAC__real *window, const FLAC__int32 L) { const FLAC__int32 N = L - 1; diff --git a/source/modules/juce_audio_formats/codecs/flac/metadata.h b/source/modules/juce_audio_formats/codecs/flac/metadata.h index 18bf6d194..02cfc3227 100644 --- a/source/modules/juce_audio_formats/codecs/flac/metadata.h +++ b/source/modules/juce_audio_formats/codecs/flac/metadata.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2001-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -500,7 +500,7 @@ FLAC_API FLAC__MetadataType FLAC__metadata_simple_iterator_get_block_type(const * \retval unsigned * The length of the metadata block at the current iterator position. * The is same length as that in the - * metadata block header, + * metadata block header, * i.e. the length of the metadata body that follows the header. */ FLAC_API unsigned FLAC__metadata_simple_iterator_get_block_length(const FLAC__Metadata_SimpleIterator *iterator); diff --git a/source/modules/juce_audio_formats/codecs/flac/ordinals.h b/source/modules/juce_audio_formats/codecs/flac/ordinals.h index c9466c5c2..0bab9c27f 100644 --- a/source/modules/juce_audio_formats/codecs/flac/ordinals.h +++ b/source/modules/juce_audio_formats/codecs/flac/ordinals.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/source/modules/juce_audio_formats/codecs/flac/stream_decoder.h b/source/modules/juce_audio_formats/codecs/flac/stream_decoder.h index be402c830..50cd7544b 100644 --- a/source/modules/juce_audio_formats/codecs/flac/stream_decoder.h +++ b/source/modules/juce_audio_formats/codecs/flac/stream_decoder.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/source/modules/juce_audio_formats/codecs/flac/stream_encoder.h b/source/modules/juce_audio_formats/codecs/flac/stream_encoder.h index f0fcab569..646efb799 100644 --- a/source/modules/juce_audio_formats/codecs/flac/stream_encoder.h +++ b/source/modules/juce_audio_formats/codecs/flac/stream_encoder.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2013 Xiph.Org Foundation + * Copyright (C) 2011-2014 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -830,28 +830,28 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_sample_rate(FLAC__StreamEncoder *en * The actual values set for each level are: * * - * + * + * + * + * + * + * + * + * + * + * + * * - * - * - * - * - * - * - * - * - * + * + * + * + * + * + * + * + * + * *
level - * do mid-side stereo - * loose mid-side stereo - * apodization - * max lpc order - * qlp coeff precision - * qlp coeff prec search - * escape coding - * exhaustive model search - * min residual partition order - * max residual partition order - * rice parameter search dist + * leveldo mid-side stereoloose mid-side stereoapodizationmax lpc orderqlp coeff precisionqlp coeff prec searchescape codingexhaustive model searchmin residual partition ordermax residual partition orderrice parameter search dist
0 false false tukey(0.5) 0 0 false false false 0 3 0
1 true true tukey(0.5) 0 0 false false false 0 3 0
2 true false tukey(0.5) 0 0 false false false 0 3 0
3 false false tukey(0.5) 6 0 false false false 0 4 0
4 true true tukey(0.5) 8 0 false false false 0 4 0
5 true false tukey(0.5) 8 0 false false false 0 5 0
6 true false tukey(0.5) 8 0 false false false 0 6 0
7 true false tukey(0.5) 8 0 false false true 0 6 0
8 true false tukey(0.5) 12 0 false false true 0 6 0
0 false false tukey(0.5) 0 0 false false false 0 3 0
1 true true tukey(0.5) 0 0 false false false 0 3 0
2 true false tukey(0.5) 0 0 false false false 0 3 0
3 false false tukey(0.5) 6 0 false false false 0 4 0
4 true true tukey(0.5) 8 0 false false false 0 4 0
5 true false tukey(0.5) 8 0 false false false 0 5 0
6 true false tukey(0.5);partial_tukey(2) 8 0 false false false 0 6 0
7 true false tukey(0.5);partial_tukey(2) 12 0 false false false 0 6 0
8 true false tukey(0.5);partial_tukey(2);punchout_tukey(3) 12 0 false false false 0 6 0
* * \default \c 5 @@ -920,7 +920,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_loose_mid_side_stereo(FLAC__StreamE * The available functions are \c bartlett, \c bartlett_hann, * \c blackman, \c blackman_harris_4term_92db, \c connes, \c flattop, * \c gauss(STDDEV), \c hamming, \c hann, \c kaiser_bessel, \c nuttall, - * \c rectangle, \c triangle, \c tukey(P), \c welch. + * \c rectangle, \c triangle, \c tukey(P), \c partial_tukey(n[/ov[/P]]), + * \c punchout_tukey(n[/ov[/P]]), \c welch. * * For \c gauss(STDDEV), STDDEV specifies the standard deviation * (0 +#include +#include +#include + +int get_utf8_argv(int *argc, char ***argv); + +int printf_utf8(const char *format, ...); +int fprintf_utf8(FILE *stream, const char *format, ...); +int vfprintf_utf8(FILE *stream, const char *format, va_list argptr); + +FILE *fopen_utf8(const char *filename, const char *mode); +int stat_utf8(const char *path, struct stat *buffer); +int _stat64_utf8(const char *path, struct __stat64 *buffer); +int chmod_utf8(const char *filename, int pmode); +int utime_utf8(const char *filename, struct utimbuf *times); +int unlink_utf8(const char *filename); +int rename_utf8(const char *oldname, const char *newname); +size_t strlen_utf8(const char *str); +int win_get_console_width(void); +int print_console(FILE *stream, const wchar_t *text, size_t len); +HANDLE WINAPI CreateFile_utf8(const char *lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif +#endif diff --git a/source/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp index 2029bc827..aac2633c7 100644 --- a/source/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp @@ -193,47 +193,72 @@ namespace AiffFileHelpers #endif //============================================================================== - static String readCATEChunk (InputStream& input, const uint32 length) + namespace CATEChunk { - MemoryBlock mb; - input.skipNextBytes (4); - input.readIntoMemoryBlock (mb, (ssize_t) length - 4); - - static const char* appleGenres[] = - { - "Rock/Blues", - "Electronic/Dance", - "Jazz", - "Urban", - "World/Ethnic", - "Cinematic/New Age", - "Orchestral", - "Country/Folk", - "Experimental", - "Other Genre", - nullptr - }; + static bool isValidTag (const char* d) noexcept + { + return CharacterFunctions::isLetterOrDigit (d[0]) && CharacterFunctions::isUpperCase (d[0]) + && CharacterFunctions::isLetterOrDigit (d[1]) && CharacterFunctions::isLowerCase (d[1]) + && CharacterFunctions::isLetterOrDigit (d[2]) && CharacterFunctions::isLowerCase (d[2]); + } - const StringArray genres (appleGenres); - StringArray tagsArray; + static bool isAppleGenre (const String& tag) noexcept + { + static const char* appleGenres[] = + { + "Rock/Blues", + "Electronic/Dance", + "Jazz", + "Urban", + "World/Ethnic", + "Cinematic/New Age", + "Orchestral", + "Country/Folk", + "Experimental", + "Other Genre" + }; + + for (int i = 0; i < numElementsInArray (appleGenres); ++i) + if (tag == appleGenres[i]) + return true; - int bytesLeft = (int) mb.getSize(); - const char* data = static_cast (mb.getData()); + return false; + } - while (bytesLeft > 0) + static String read (InputStream& input, const uint32 length) { - const String tag (CharPointer_UTF8 (data), - CharPointer_UTF8 (data + bytesLeft)); + MemoryBlock mb; + input.skipNextBytes (4); + input.readIntoMemoryBlock (mb, (ssize_t) length - 4); - if (tag.isNotEmpty()) - tagsArray.add (data); + StringArray tagsArray; - const int numBytesInTag = genres.contains (tag) ? 118 : 50; - data += numBytesInTag; - bytesLeft -= numBytesInTag; - } + const char* data = static_cast (mb.getData()); + const char* dataEnd = data + mb.getSize(); + + while (data < dataEnd) + { + bool isGenre = false; - return tagsArray.joinIntoString (";"); + if (isValidTag (data)) + { + const String tag = String (CharPointer_UTF8 (data), CharPointer_UTF8 (dataEnd)); + isGenre = isAppleGenre (tag); + tagsArray.add (tag); + } + + data += isGenre ? 118 : 50; + + if (data[0] == 0) + { + if (data + 52 < dataEnd && isValidTag (data + 50)) data += 50; + else if (data + 120 < dataEnd && isValidTag (data + 118)) data += 118; + else if (data + 170 < dataEnd && isValidTag (data + 168)) data += 168; + } + } + + return tagsArray.joinIntoString (";"); + } } //============================================================================== @@ -518,7 +543,7 @@ public: else if (type == chunkName ("cate")) { metadataValues.set (AiffAudioFormat::appleTag, - AiffFileHelpers::readCATEChunk (*input, length));; + AiffFileHelpers::CATEChunk::read (*input, length)); } else if ((hasGotVer && hasGotData && hasGotType) || chunkEnd < input->getPosition() @@ -821,12 +846,54 @@ public: return true; } - void readMaxLevels (int64 startSampleInFile, int64 numSamples, - float& min0, float& max0, float& min1, float& max1) + void getSample (int64 sample, float* result) const noexcept override + { + const int num = (int) numChannels; + + if (map == nullptr || ! mappedSection.contains (sample)) + { + jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. + + zeromem (result, sizeof (float) * (size_t) num); + return; + } + + float** dest = &result; + const void* source = sampleToPointer (sample); + + if (littleEndian) + { + switch (bitsPerSample) + { + case 8: ReadHelper::read (dest, 0, 1, source, 1, num); break; + case 16: ReadHelper::read (dest, 0, 1, source, 1, num); break; + case 24: ReadHelper::read (dest, 0, 1, source, 1, num); break; + case 32: if (usesFloatingPointData) ReadHelper::read (dest, 0, 1, source, 1, num); + else ReadHelper::read (dest, 0, 1, source, 1, num); break; + default: jassertfalse; break; + } + } + else + { + switch (bitsPerSample) + { + case 8: ReadHelper::read (dest, 0, 1, source, 1, num); break; + case 16: ReadHelper::read (dest, 0, 1, source, 1, num); break; + case 24: ReadHelper::read (dest, 0, 1, source, 1, num); break; + case 32: if (usesFloatingPointData) ReadHelper::read (dest, 0, 1, source, 1, num); + else ReadHelper::read (dest, 0, 1, source, 1, num); break; + default: jassertfalse; break; + } + } + } + + void readMaxLevels (int64 startSampleInFile, int64 numSamples, Range* results, int numChannelsToRead) override { if (numSamples <= 0) { - min0 = max0 = min1 = max1 = 0; + for (int i = 0; i < numChannelsToRead; ++i) + results[i] = Range(); + return; } @@ -834,17 +901,19 @@ public: { jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. - min0 = max0 = min1 = max1 = 0; + for (int i = 0; i < numChannelsToRead; ++i) + results[i] = Range(); + return; } switch (bitsPerSample) { - case 8: scanMinAndMax (startSampleInFile, numSamples, min0, max0, min1, max1); break; - case 16: scanMinAndMax (startSampleInFile, numSamples, min0, max0, min1, max1); break; - case 24: scanMinAndMax (startSampleInFile, numSamples, min0, max0, min1, max1); break; - case 32: if (usesFloatingPointData) scanMinAndMax (startSampleInFile, numSamples, min0, max0, min1, max1); - else scanMinAndMax (startSampleInFile, numSamples, min0, max0, min1, max1); break; + case 8: scanMinAndMax (startSampleInFile, numSamples, results, numChannelsToRead); break; + case 16: scanMinAndMax (startSampleInFile, numSamples, results, numChannelsToRead); break; + case 24: scanMinAndMax (startSampleInFile, numSamples, results, numChannelsToRead); break; + case 32: if (usesFloatingPointData) scanMinAndMax (startSampleInFile, numSamples, results, numChannelsToRead); + else scanMinAndMax (startSampleInFile, numSamples, results, numChannelsToRead); break; default: jassertfalse; break; } } @@ -853,24 +922,17 @@ private: const bool littleEndian; template - void scanMinAndMax (int64 startSampleInFile, int64 numSamples, - float& min0, float& max0, float& min1, float& max1) const noexcept + void scanMinAndMax (int64 startSampleInFile, int64 numSamples, Range* results, int numChannelsToRead) const noexcept { - scanMinAndMax2 (0, startSampleInFile, numSamples, min0, max0); - - if (numChannels > 1) - scanMinAndMax2 (1, startSampleInFile, numSamples, min1, max1); - else - min1 = max1 = 0; + for (int i = 0; i < numChannelsToRead; ++i) + results[i] = scanMinAndMaxForChannel (i, startSampleInFile, numSamples); } template - void scanMinAndMax2 (int channel, int64 startSampleInFile, int64 numSamples, float& mn, float& mx) const noexcept + Range scanMinAndMaxForChannel (int channel, int64 startSampleInFile, int64 numSamples) const noexcept { - if (littleEndian) - scanMinAndMaxInterleaved (channel, startSampleInFile, numSamples, mn, mx); - else - scanMinAndMaxInterleaved (channel, startSampleInFile, numSamples, mn, mx); + return littleEndian ? scanMinAndMaxInterleaved (channel, startSampleInFile, numSamples) + : scanMinAndMaxInterleaved (channel, startSampleInFile, numSamples); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedAiffReader) diff --git a/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp index 219c93f70..6551f1f53 100644 --- a/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp @@ -607,7 +607,7 @@ private: float* costab = cosTables[i]; for (int k = 0; k < kr; ++k) - costab[k] = (float) (1.0 / (2.0 * cos (double_Pi * (k * 2 + 1) / divv))); + costab[k] = (float) (1.0 / (2.0 * std::cos (double_Pi * (k * 2 + 1) / divv))); } for (i = 0, j = 0; i < 256; ++i, ++j, table += 32) @@ -692,23 +692,23 @@ private: for (i = 0; i < 18; ++i) { - win[0][i] = win[1][i] = (float) (0.5 * sin (double_Pi / 72.0 * (2 * i + 1)) / cos (double_Pi * (2 * i + 19) / 72.0)); - win[0][i + 18] = win[3][i + 18] = (float) (0.5 * sin (double_Pi / 72.0 * (2 * (i + 18) + 1)) / cos (double_Pi * (2 * (i + 18) + 19) / 72.0)); + win[0][i] = win[1][i] = (float) (0.5 * std::sin (double_Pi / 72.0 * (2 * i + 1)) / std::cos (double_Pi * (2 * i + 19) / 72.0)); + win[0][i + 18] = win[3][i + 18] = (float) (0.5 * std::sin (double_Pi / 72.0 * (2 * (i + 18) + 1)) / std::cos (double_Pi * (2 * (i + 18) + 19) / 72.0)); } const double piOver72 = double_Pi; for (i = 0; i < 6; ++i) { - win[1][i + 18] = (float) (0.5 / cos (piOver72 * (2 * (i + 18) + 19))); - win[3][i + 12] = (float) (0.5 / cos (piOver72 * (2 * (i + 12) + 19))); - win[1][i + 24] = (float) (0.5 * sin (double_Pi / 24.0 * (2 * i + 13)) / cos (piOver72 * (2 * (i + 24) + 19))); + win[1][i + 18] = (float) (0.5 / std::cos (piOver72 * (2 * (i + 18) + 19))); + win[3][i + 12] = (float) (0.5 / std::cos (piOver72 * (2 * (i + 12) + 19))); + win[1][i + 24] = (float) (0.5 * std::sin (double_Pi / 24.0 * (2 * i + 13)) / std::cos (piOver72 * (2 * (i + 24) + 19))); win[1][i + 30] = win[3][i] = 0; - win[3][i + 6] = (float) (0.5 * sin (double_Pi / 24.0 * (2 * i + 1)) / cos (piOver72 * (2 * (i + 6) + 19))); + win[3][i + 6] = (float) (0.5 * std::sin (double_Pi / 24.0 * (2 * i + 1)) / std::cos (piOver72 * (2 * (i + 6) + 19))); } for (i = 0; i < 12; ++i) - win[2][i] = (float) (0.5 * sin (double_Pi / 24.0 * (2 * i + 1)) / cos (double_Pi * (2 * i + 7) / 24.0)); + win[2][i] = (float) (0.5 * std::sin (double_Pi / 24.0 * (2 * i + 1)) / std::cos (double_Pi * (2 * i + 7) / 24.0)); for (j = 0; j < 4; ++j) { @@ -721,7 +721,7 @@ private: for (i = 0; i < 16; ++i) { - const double t = tan (i * double_Pi / 12.0); + const double t = std::tan (i * double_Pi / 12.0); tan1_1[i] = (float) (t / (1.0 + t)); tan2_1[i] = (float) (1.0 / (1.0 + t)); tan1_2[i] = (float) (sqrt2 * t / (1.0 + t)); diff --git a/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp index 473e7445b..1929e4cbf 100644 --- a/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp @@ -1289,12 +1289,39 @@ public: return true; } - void readMaxLevels (int64 startSampleInFile, int64 numSamples, - float& min0, float& max0, float& min1, float& max1) override + void getSample (int64 sample, float* result) const noexcept override + { + const int num = (int) numChannels; + + if (map == nullptr || ! mappedSection.contains (sample)) + { + jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. + + zeromem (result, sizeof (float) * (size_t) num); + return; + } + + float** dest = &result; + const void* source = sampleToPointer (sample); + + switch (bitsPerSample) + { + case 8: ReadHelper::read (dest, 0, 1, source, 1, num); break; + case 16: ReadHelper::read (dest, 0, 1, source, 1, num); break; + case 24: ReadHelper::read (dest, 0, 1, source, 1, num); break; + case 32: if (usesFloatingPointData) ReadHelper::read (dest, 0, 1, source, 1, num); + else ReadHelper::read (dest, 0, 1, source, 1, num); break; + default: jassertfalse; break; + } + } + + void readMaxLevels (int64 startSampleInFile, int64 numSamples, Range* results, int numChannelsToRead) override { if (numSamples <= 0) { - min0 = max0 = min1 = max1 = 0; + for (int i = 0; i < numChannelsToRead; ++i) + results[i] = Range(); + return; } @@ -1302,32 +1329,29 @@ public: { jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. - min0 = max0 = min1 = max1 = 0; + for (int i = 0; i < numChannelsToRead; ++i) + results[i] = Range(); + return; } switch (bitsPerSample) { - case 8: scanMinAndMax (startSampleInFile, numSamples, min0, max0, min1, max1); break; - case 16: scanMinAndMax (startSampleInFile, numSamples, min0, max0, min1, max1); break; - case 24: scanMinAndMax (startSampleInFile, numSamples, min0, max0, min1, max1); break; - case 32: if (usesFloatingPointData) scanMinAndMax (startSampleInFile, numSamples, min0, max0, min1, max1); - else scanMinAndMax (startSampleInFile, numSamples, min0, max0, min1, max1); break; + case 8: scanMinAndMax (startSampleInFile, numSamples, results, numChannelsToRead); break; + case 16: scanMinAndMax (startSampleInFile, numSamples, results, numChannelsToRead); break; + case 24: scanMinAndMax (startSampleInFile, numSamples, results, numChannelsToRead); break; + case 32: if (usesFloatingPointData) scanMinAndMax (startSampleInFile, numSamples, results, numChannelsToRead); + else scanMinAndMax (startSampleInFile, numSamples, results, numChannelsToRead); break; default: jassertfalse; break; } } private: template - void scanMinAndMax (int64 startSampleInFile, int64 numSamples, - float& min0, float& max0, float& min1, float& max1) const noexcept + void scanMinAndMax (int64 startSampleInFile, int64 numSamples, Range* results, int numChannelsToRead) const noexcept { - scanMinAndMaxInterleaved (0, startSampleInFile, numSamples, min0, max0); - - if (numChannels > 1) - scanMinAndMaxInterleaved (1, startSampleInFile, numSamples, min1, max1); - else - min1 = max1 = 0; + for (int i = 0; i < numChannelsToRead; ++i) + results[i] = scanMinAndMaxInterleaved (i, startSampleInFile, numSamples); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedWavReader) diff --git a/source/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp b/source/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp index 5db3149ef..b265b0939 100644 --- a/source/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp +++ b/source/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp @@ -46,6 +46,7 @@ bool AudioFormatReader::read (int* const* destSamples, { jassert (numDestChannels > 0); // you have to actually give this some channels to work with! + const size_t originalNumSamplesToRead = (size_t) numSamplesToRead; int startOffsetInDestBuffer = 0; if (startSampleInSource < 0) @@ -64,7 +65,7 @@ bool AudioFormatReader::read (int* const* destSamples, if (numSamplesToRead <= 0) return true; - if (! readSamples (const_cast (destSamples), + if (! readSamples (const_cast (destSamples), jmin ((int) numChannels, numDestChannels), startOffsetInDestBuffer, startSampleInSource, numSamplesToRead)) return false; @@ -87,13 +88,13 @@ bool AudioFormatReader::read (int* const* destSamples, if (lastFullChannel != nullptr) for (int i = (int) numChannels; i < numDestChannels; ++i) if (destSamples[i] != nullptr) - memcpy (destSamples[i], lastFullChannel, sizeof (int) * (size_t) numSamplesToRead); + memcpy (destSamples[i], lastFullChannel, sizeof (int) * originalNumSamplesToRead); } else { for (int i = (int) numChannels; i < numDestChannels; ++i) if (destSamples[i] != nullptr) - zeromem (destSamples[i], sizeof (int) * (size_t) numSamplesToRead); + zeromem (destSamples[i], sizeof (int) * originalNumSamplesToRead); } } @@ -229,20 +230,21 @@ void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples float& lowestRight, float& highestRight) { Range levels[2]; - readMaxLevels (startSampleInFile, numSamples, levels, jmin (2, (int) numChannels)); - lowestLeft = levels[0].getStart(); - highestLeft = levels[0].getEnd(); - if (numChannels > 1) + if (numChannels < 2) { - lowestRight = levels[1].getStart(); - highestRight = levels[1].getEnd(); + readMaxLevels (startSampleInFile, numSamples, levels, (int) numChannels); + levels[1] = levels[0]; } else { - lowestRight = lowestLeft; - highestRight = highestLeft; + readMaxLevels (startSampleInFile, numSamples, levels, 2); } + + lowestLeft = levels[0].getStart(); + highestLeft = levels[0].getEnd(); + lowestRight = levels[1].getStart(); + highestRight = levels[1].getEnd(); } int64 AudioFormatReader::searchForLevel (int64 startSample, diff --git a/source/modules/juce_audio_formats/format/juce_AudioFormatReader.h b/source/modules/juce_audio_formats/format/juce_AudioFormatReader.h index 065388a77..9d63f03d4 100644 --- a/source/modules/juce_audio_formats/format/juce_AudioFormatReader.h +++ b/source/modules/juce_audio_formats/format/juce_AudioFormatReader.h @@ -44,7 +44,7 @@ protected: @param sourceStream the stream to read from - this will be deleted by this object when it is no longer needed. (Some specialised readers might not use this parameter and - can leave it as 0). + can leave it as nullptr). @param formatName the description that will be returned by the getFormatName() method */ @@ -247,8 +247,8 @@ protected: template struct ReadHelper { - typedef AudioData::Pointer DestType; - typedef AudioData::Pointer SourceType; + typedef AudioData::Pointer DestType; + typedef AudioData::Pointer SourceType; template static void read (TargetType* const* destData, int destOffset, int numDestChannels, diff --git a/source/modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp b/source/modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp index bbe1ebd79..296f55f86 100644 --- a/source/modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp +++ b/source/modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp @@ -57,17 +57,10 @@ bool AudioSubsectionReader::readSamples (int** destSamples, int numDestChannels, startSampleInFile + startSample, numSamples); } -void AudioSubsectionReader::readMaxLevels (int64 startSampleInFile, - int64 numSamples, - float& lowestLeft, - float& highestLeft, - float& lowestRight, - float& highestRight) +void AudioSubsectionReader::readMaxLevels (int64 startSampleInFile, int64 numSamples, Range* results, int numChannelsToRead) { startSampleInFile = jmax ((int64) 0, startSampleInFile); numSamples = jmax ((int64) 0, jmin (numSamples, length - startSampleInFile)); - source->readMaxLevels (startSampleInFile + startSample, numSamples, - lowestLeft, highestLeft, - lowestRight, highestRight); + source->readMaxLevels (startSampleInFile + startSample, numSamples, results, numChannelsToRead); } diff --git a/source/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h b/source/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h index fb9e6a031..a666f4f0e 100644 --- a/source/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h +++ b/source/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h @@ -68,8 +68,7 @@ public: int64 startSampleInFile, int numSamples) override; void readMaxLevels (int64 startSample, int64 numSamples, - float& lowestLeft, float& highestLeft, - float& lowestRight, float& highestRight) override; + Range* results, int numChannelsToRead) override; private: diff --git a/source/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp b/source/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp index ecfe1d457..38fc70435 100644 --- a/source/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp +++ b/source/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp @@ -28,7 +28,8 @@ BufferingAudioReader::BufferingAudioReader (AudioFormatReader* sourceReader, : AudioFormatReader (nullptr, sourceReader->getFormatName()), source (sourceReader), thread (timeSliceThread), nextReadPosition (0), - numBlocks (1 + (samplesToBuffer / samplesPerBlock)) + numBlocks (1 + (samplesToBuffer / samplesPerBlock)), + timeoutMs (0) { sampleRate = source->sampleRate; lengthInSamples = source->lengthInSamples; diff --git a/source/modules/juce_audio_formats/format/juce_MemoryMappedAudioFormatReader.h b/source/modules/juce_audio_formats/format/juce_MemoryMappedAudioFormatReader.h index 2ec6388be..f245a02ce 100644 --- a/source/modules/juce_audio_formats/format/juce_MemoryMappedAudioFormatReader.h +++ b/source/modules/juce_audio_formats/format/juce_MemoryMappedAudioFormatReader.h @@ -70,6 +70,12 @@ public: /** Touches the memory for the given sample, to force it to be loaded into active memory. */ void touchSample (int64 sample) const noexcept; + /** Returns the samples for all channels at a given sample position. + The result array must be large enough to hold a value for each channel + that this reader contains. + */ + virtual void getSample (int64 sampleIndex, float* result) const noexcept = 0; + /** Returns the number of bytes currently being mapped */ size_t getNumBytesUsed() const { return map != nullptr ? map->getSize() : 0; } @@ -91,12 +97,12 @@ protected: /** Used by AudioFormatReader subclasses to scan for min/max ranges in interleaved data. */ template - void scanMinAndMaxInterleaved (int channel, int64 startSampleInFile, int64 numSamples, float& mn, float& mx) const noexcept + Range scanMinAndMaxInterleaved (int channel, int64 startSampleInFile, int64 numSamples) const noexcept { typedef AudioData::Pointer SourceType; - SourceType (addBytesToPointer (sampleToPointer (startSampleInFile), ((int) bitsPerSample / 8) * channel), (int) numChannels) - .findMinAndMax ((size_t) numSamples, mn, mx); + return SourceType (addBytesToPointer (sampleToPointer (startSampleInFile), ((int) bitsPerSample / 8) * channel), (int) numChannels) + .findMinAndMax ((size_t) numSamples); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedAudioFormatReader) diff --git a/source/modules/juce_audio_formats/sampler/juce_Sampler.cpp b/source/modules/juce_audio_formats/sampler/juce_Sampler.cpp index 4e7583e0a..d2f1ace73 100644 --- a/source/modules/juce_audio_formats/sampler/juce_Sampler.cpp +++ b/source/modules/juce_audio_formats/sampler/juce_Sampler.cpp @@ -119,7 +119,7 @@ void SamplerVoice::startNote (const int midiNoteNumber, if (sound->releaseSamples > 0) releaseDelta = (float) (-pitchRatio / sound->releaseSamples); else - releaseDelta = 0.0f; + releaseDelta = -1.0f; } else { diff --git a/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp b/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp index 8170fcafc..cb4d88424 100644 --- a/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp +++ b/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp @@ -364,7 +364,6 @@ class VST3HostContext : public Vst::IComponentHandler, // From VST V3.0.0 public Vst::IComponentHandler3, // From VST V3.5.0 (also very well named!) public Vst::IContextMenuTarget, public Vst::IHostApplication, - public Vst::IParamValueQueue, public Vst::IUnitHandler { public: @@ -684,31 +683,6 @@ public: return kNotImplemented; } - //============================================================================== - Vst::ParamID PLUGIN_API getParameterId() override - { - jassertfalse; - return 0; - } - - Steinberg::int32 PLUGIN_API getPointCount() override - { - jassertfalse; - return 0; - } - - tresult PLUGIN_API getPoint (Steinberg::int32, Steinberg::int32&, Vst::ParamValue&) override - { - jassertfalse; - return kResultFalse; - } - - tresult PLUGIN_API addPoint (Steinberg::int32, Vst::ParamValue, Steinberg::int32&) override - { - jassertfalse; - return kResultFalse; - } - //============================================================================== tresult PLUGIN_API notifyUnitSelection (Vst::UnitID) override { @@ -736,7 +710,6 @@ public: TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IComponentHandler3) TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IContextMenuTarget) TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IHostApplication) - TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IParamValueQueue) TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IUnitHandler) TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (iid, FUnknown, Vst::IComponentHandler) @@ -837,47 +810,26 @@ private: //============================================================================== tresult PLUGIN_API setInt (AttrID id, Steinberg::int64 value) override { - jassert (id != nullptr); - - if (! setValueForId (id, value)) - owner->messageQueue.add (ComSmartPtr (new Message (*owner, this, id, value))); - + addMessageToQueue (id, value); return kResultTrue; } tresult PLUGIN_API setFloat (AttrID id, double value) override { - jassert (id != nullptr); - - if (! setValueForId (id, value)) - owner->messageQueue.add (ComSmartPtr (new Message (*owner, this, id, value))); - + addMessageToQueue (id, value); return kResultTrue; } tresult PLUGIN_API setString (AttrID id, const Vst::TChar* string) override { - jassert (id != nullptr); - jassert (string != nullptr); - - const String text (toString (string)); - - if (! setValueForId (id, text)) - owner->messageQueue.add (ComSmartPtr (new Message (*owner, this, id, text))); - + addMessageToQueue (id, toString (string)); return kResultTrue; } tresult PLUGIN_API setBinary (AttrID id, const void* data, Steinberg::uint32 size) override { - jassert (id != nullptr); - jassert (data != nullptr && size > 0); - - MemoryBlock block (data, (size_t) size); - - if (! setValueForId (id, block)) - owner->messageQueue.add (ComSmartPtr (new Message (*owner, this, id, block))); - + jassert (size >= 0 && (data != nullptr || size == 0)); + addMessageToQueue (id, MemoryBlock (data, (size_t) size)); return kResultTrue; } @@ -886,7 +838,7 @@ private: { jassert (id != nullptr); - if (fetchValueForId (id, result)) + if (findMessageOnQueueWithID (id, result)) return kResultTrue; jassertfalse; @@ -897,7 +849,7 @@ private: { jassert (id != nullptr); - if (fetchValueForId (id, result)) + if (findMessageOnQueueWithID (id, result)) return kResultTrue; jassertfalse; @@ -909,7 +861,7 @@ private: jassert (id != nullptr); String stringToFetch; - if (fetchValueForId (id, stringToFetch)) + if (findMessageOnQueueWithID (id, stringToFetch)) { Steinberg::String str (stringToFetch.toRawUTF8()); str.copyTo (result, 0, (Steinberg::int32) jmin (length, (Steinberg::uint32) std::numeric_limits::max())); @@ -949,7 +901,7 @@ private: //============================================================================== template - bool setValueForId (AttrID id, const Type& value) + void addMessageToQueue (AttrID id, const Type& value) { jassert (id != nullptr); @@ -960,15 +912,15 @@ private: if (std::strcmp (message->getMessageID(), id) == 0) { message->value = value; - return true; + return; } } - return false; // No message found with that Id + owner->messageQueue.add (ComSmartPtr (new Message (*owner, this, id, value))); } template - bool fetchValueForId (AttrID id, Type& value) + bool findMessageOnQueueWithID (AttrID id, Type& value) { jassert (id != nullptr); @@ -1632,8 +1584,8 @@ public: : module (handle), numInputAudioBusses (0), numOutputAudioBusses (0), - inputParameterChanges (new ParameterChangeList()), - outputParameterChanges (new ParameterChangeList()), + inputParameterChanges (new ParamValueQueueList()), + outputParameterChanges (new ParamValueQueueList()), midiInputs (new MidiEventList()), midiOutputs (new MidiEventList()), isComponentInitialised (false), @@ -1847,6 +1799,8 @@ public: processor->process (data); MidiEventList::toMidiBuffer (midiMessages, *midiOutputs); + + inputParameterChanges->clearAllQueues(); } } @@ -1977,8 +1931,11 @@ public: { if (editController != nullptr) { - const uint32 id = getParameterInfoForIndex (parameterIndex).id; - editController->setParamNormalized (id, (double) newValue); + const uint32 paramID = getParameterInfoForIndex (parameterIndex).id; + editController->setParamNormalized (paramID, (double) newValue); + + Steinberg::int32 index; + inputParameterChanges->addParameterData (paramID, index)->addPoint (0, newValue, index); } } @@ -2053,6 +2010,115 @@ public: (void) sizeInBytes; } + //============================================================================== + // NB: this class and its subclasses must be public to avoid problems in + // DLL builds under MSVC. + class ParamValueQueueList : public Vst::IParameterChanges + { + public: + ParamValueQueueList() {} + virtual ~ParamValueQueueList() {} + + JUCE_DECLARE_VST3_COM_REF_METHODS + JUCE_DECLARE_VST3_COM_QUERY_METHODS + + Steinberg::int32 PLUGIN_API getParameterCount() override { return (Steinberg::int32) queues.size(); } + Vst::IParamValueQueue* PLUGIN_API getParameterData (Steinberg::int32 index) override { return queues[(int) index]; } + + Vst::IParamValueQueue* PLUGIN_API addParameterData (const Vst::ParamID& id, Steinberg::int32& index) override + { + for (int i = queues.size(); --i >= 0;) + { + if (queues.getUnchecked (i)->getParameterId() == id) + { + index = (Steinberg::int32) i; + return queues.getUnchecked (i); + } + } + + index = getParameterCount(); + return queues.add (new ParamValueQueue (id)); + } + + void clearAllQueues() noexcept + { + for (int i = queues.size(); --i >= 0;) + queues.getUnchecked (i)->clear(); + } + + struct ParamValueQueue : public Vst::IParamValueQueue + { + ParamValueQueue (Vst::ParamID parameterID) : paramID (parameterID) + { + points.ensureStorageAllocated (1024); + } + + virtual ~ParamValueQueue() {} + + JUCE_DECLARE_VST3_COM_REF_METHODS + JUCE_DECLARE_VST3_COM_QUERY_METHODS + + Steinberg::Vst::ParamID PLUGIN_API getParameterId() override { return paramID; } + Steinberg::int32 PLUGIN_API getPointCount() override { return (Steinberg::int32) points.size(); } + + Steinberg::tresult PLUGIN_API getPoint (Steinberg::int32 index, + Steinberg::int32& sampleOffset, + Steinberg::Vst::ParamValue& value) override + { + const ScopedLock sl (pointLock); + + if (isPositiveAndBelow ((int) index, points.size())) + { + ParamPoint e (points.getUnchecked ((int) index)); + sampleOffset = e.sampleOffset; + value = e.value; + return kResultTrue; + } + + sampleOffset = -1; + value = 0.0; + return kResultFalse; + } + + Steinberg::tresult PLUGIN_API addPoint (Steinberg::int32 sampleOffset, + Steinberg::Vst::ParamValue value, + Steinberg::int32& index) override + { + ParamPoint p = { sampleOffset, value }; + + const ScopedLock sl (pointLock); + index = (Steinberg::int32) points.size(); + points.add (p); + return kResultTrue; + } + + void clear() noexcept + { + const ScopedLock sl (pointLock); + points.clearQuick(); + } + + private: + struct ParamPoint + { + Steinberg::int32 sampleOffset; + Steinberg::Vst::ParamValue value; + }; + + Atomic refCount; + const Vst::ParamID paramID; + Array points; + CriticalSection pointLock; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParamValueQueue) + }; + + Atomic refCount; + OwnedArray queues; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParamValueQueueList) + }; + private: //============================================================================== VST3ModuleHandle::Ptr module; @@ -2098,7 +2164,7 @@ private: if (object->getState (&stream) == kResultTrue) { - MemoryBlock info (stream.getData(), (std::size_t) stream.getSize()); + MemoryBlock info (stream.getData(), (size_t) stream.getSize()); head.createNewChildElement (identifier)->addTextElement (info.toBase64Encoding()); } } @@ -2123,36 +2189,7 @@ private: return stream; } - //============================================================================== - class ParameterChangeList : public Vst::IParameterChanges - { - public: - ParameterChangeList() {} - virtual ~ParameterChangeList() {} - - JUCE_DECLARE_VST3_COM_REF_METHODS - JUCE_DECLARE_VST3_COM_QUERY_METHODS - - Steinberg::int32 PLUGIN_API getParameterCount() override { return 0; } - - Vst::IParamValueQueue* PLUGIN_API getParameterData (Steinberg::int32) override - { - return nullptr; - } - - Vst::IParamValueQueue* PLUGIN_API addParameterData (const Vst::ParamID&, Steinberg::int32& index) override - { - index = 0; - return nullptr; - } - - private: - Atomic refCount; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParameterChangeList) - }; - - ComSmartPtr inputParameterChanges, outputParameterChanges; + ComSmartPtr inputParameterChanges, outputParameterChanges; ComSmartPtr midiInputs, midiOutputs; Vst::ProcessContext timingInfo; //< Only use this in processBlock()! bool isComponentInitialised, isControllerInitialised, isActive; diff --git a/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp b/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp index 5509e2c38..38590613e 100644 --- a/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp +++ b/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp @@ -135,7 +135,14 @@ namespace char chunk[8]; // variable }; - static VstInt32 fxbName (const char* name) noexcept { return (VstInt32) ByteOrder::bigEndianInt (name); } + // Compares a magic value in either endianness. + static bool compareMagic (VstInt32 magic, const char* name) noexcept + { + return magic == (VstInt32) ByteOrder::littleEndianInt (name) + || magic == (VstInt32) ByteOrder::bigEndianInt (name); + } + + static VstInt32 fxbName (const char* name) noexcept { return (VstInt32) ByteOrder::littleEndianInt (name); } static VstInt32 fxbSwap (const VstInt32 x) noexcept { return (VstInt32) ByteOrder::swapIfLittleEndian ((uint32) x); } static float fxbSwapFloat (const float x) noexcept @@ -1013,12 +1020,12 @@ public: if (position.isLooping) { vstHostTime.cycleStartPos = position.ppqLoopStart; - vstHostTime.cycleEndPos = position.ppqLoopEnd; - vstHostTime.flags |= kVstCyclePosValid; + vstHostTime.cycleEndPos = position.ppqLoopEnd; + vstHostTime.flags |= (kVstCyclePosValid | kVstTransportCycleActive); } else { - vstHostTime.flags &= ~kVstCyclePosValid; + vstHostTime.flags &= ~(kVstCyclePosValid | kVstTransportCycleActive); } } @@ -1432,11 +1439,10 @@ public: const fxSet* const set = (const fxSet*) data; - if ((set->chunkMagic != fxbName ("CcnK") && set->chunkMagic != fxbName ("KncC")) - || fxbSwap (set->version) > fxbVersionNum) + if ((! compareMagic (set->chunkMagic, "CcnK")) || fxbSwap (set->version) > fxbVersionNum) return false; - if (set->fxMagic == fxbName ("FxBk")) + if (compareMagic (set->fxMagic, "FxBk")) { // bank of programs if (fxbSwap (set->numPrograms) >= 0) @@ -1472,12 +1478,12 @@ public: return false; } } - else if (set->fxMagic == fxbName ("FxCk")) + else if (compareMagic (set->fxMagic, "FxCk")) { // single program const fxProgram* const prog = (const fxProgram*) data; - if (prog->chunkMagic != fxbName ("CcnK")) + if (! compareMagic (prog->chunkMagic, "CcnK")) return false; changeProgramName (getCurrentProgram(), prog->prgName); @@ -1485,7 +1491,7 @@ public: for (int i = 0; i < fxbSwap (prog->numParams); ++i) setParameter (i, fxbSwapFloat (prog->params[i])); } - else if (set->fxMagic == fxbName ("FBCh") || set->fxMagic == fxbName ("hCBF")) + else if (compareMagic (set->fxMagic, "FBCh")) { // non-preset chunk const fxChunkSet* const cset = (const fxChunkSet*) data; @@ -1495,7 +1501,7 @@ public: setChunkData (cset->chunk, fxbSwap (cset->chunkSize), false); } - else if (set->fxMagic == fxbName ("FPCh") || set->fxMagic == fxbName ("hCPF")) + else if (compareMagic (set->fxMagic, "FPCh")) { // preset chunk const fxProgramSet* const cset = (const fxProgramSet*) data; @@ -1671,7 +1677,8 @@ private: bool restoreProgramSettings (const fxProgram* const prog) { - if (prog->chunkMagic == fxbName ("CcnK") && prog->fxMagic == fxbName ("FxCk")) + if (compareMagic (prog->chunkMagic, "CcnK") + && compareMagic (prog->fxMagic, "FxCk")) { changeProgramName (getCurrentProgram(), prog->prgName); diff --git a/source/modules/juce_audio_processors/juce_audio_processors.cpp b/source/modules/juce_audio_processors/juce_audio_processors.cpp index efdddee27..d1f1679fb 100644 --- a/source/modules/juce_audio_processors/juce_audio_processors.cpp +++ b/source/modules/juce_audio_processors/juce_audio_processors.cpp @@ -79,71 +79,61 @@ static inline bool arrayContainsPlugin (const OwnedArray& lis #if JUCE_MAC //============================================================================== -struct AutoResizingNSViewComponent : public NSViewComponent, - private AsyncUpdater { - AutoResizingNSViewComponent(); - void childBoundsChanged(Component*) override; - void handleAsyncUpdate() override; - bool recursive; -}; - -struct AutoResizingNSViewComponentWithParent : public AutoResizingNSViewComponent, - private Timer { - AutoResizingNSViewComponentWithParent(); - NSView* getChildView() const; - void timerCallback() override; -}; - -//============================================================================== -AutoResizingNSViewComponent::AutoResizingNSViewComponent() - : recursive (false) {} - -void AutoResizingNSViewComponent::childBoundsChanged(Component*) +struct AutoResizingNSViewComponent : public NSViewComponent, + private AsyncUpdater { - if (recursive) - { - triggerAsyncUpdate(); - } - else + AutoResizingNSViewComponent() : recursive (false) {} + + void childBoundsChanged (Component*) override { - recursive = true; - resizeToFitView(); - recursive = true; + if (recursive) + { + triggerAsyncUpdate(); + } + else + { + recursive = true; + resizeToFitView(); + recursive = true; + } } -} -void AutoResizingNSViewComponent::handleAsyncUpdate() -{ - resizeToFitView(); -} + void handleAsyncUpdate() override { resizeToFitView(); } + + bool recursive; +}; //============================================================================== -AutoResizingNSViewComponentWithParent::AutoResizingNSViewComponentWithParent() +struct AutoResizingNSViewComponentWithParent : public AutoResizingNSViewComponent, + private Timer { - NSView* v = [[NSView alloc] init]; - setView (v); - [v release]; + AutoResizingNSViewComponentWithParent() + { + NSView* v = [[NSView alloc] init]; + setView (v); + [v release]; - startTimer(500); -} + startTimer (30); + } -NSView* AutoResizingNSViewComponentWithParent::getChildView() const -{ - if (NSView* parent = (NSView*)getView()) - if ([[parent subviews] count] > 0) - return [[parent subviews] objectAtIndex: 0]; + NSView* getChildView() const + { + if (NSView* parent = (NSView*) getView()) + if ([[parent subviews] count] > 0) + return [[parent subviews] objectAtIndex: 0]; - return nil; -} + return nil; + } -void AutoResizingNSViewComponentWithParent::timerCallback() -{ - if (NSView* child = getChildView()) + void timerCallback() override { - stopTimer(); - setView(child); + if (NSView* child = getChildView()) + { + stopTimer(); + setView (child); + } } -} +}; #endif #if JUCE_CLANG diff --git a/source/modules/juce_audio_processors/processors/juce_AudioPlayHead.h b/source/modules/juce_audio_processors/processors/juce_AudioPlayHead.h index b92b802f9..e3247b52b 100644 --- a/source/modules/juce_audio_processors/processors/juce_AudioPlayHead.h +++ b/source/modules/juce_audio_processors/processors/juce_AudioPlayHead.h @@ -130,8 +130,10 @@ public: /** Fills-in the given structure with details about the transport's position at the start of the current processing block. - This method must ONLY be called from within your AudioProcessor::processBlock() - method. Calling it at any other time will probably cause a nasty crash. + 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 + in which a time would make sense, and some hosts will almost certainly have + multithreading issues if it's not called on the audio thread. */ virtual bool getCurrentPosition (CurrentPositionInfo& result) = 0; }; diff --git a/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h b/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h index 9fe9911db..0846e3379 100644 --- a/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h +++ b/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h @@ -140,11 +140,17 @@ public: /** Returns the current AudioPlayHead object that should be used to find out the state and position of the playhead. - You can call this from your processBlock() method, and use the AudioPlayHead - object to get the details about the time of the start of the block currently - being processed. + 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 + in which a time would make sense, and some hosts will almost certainly have + multithreading issues if it's not called on the audio thread. - If the host hasn't supplied a playhead object, this will return nullptr. + The AudioPlayHead object that is returned can be used to get the details about + the time of the start of the block currently being processed. But do not + store this pointer or use it outside of the current audio callback, because + the host may delete or re-use it. + + If the host can't or won't provide any time info, this will return nullptr. */ AudioPlayHead* getPlayHead() const noexcept { return playHead; } diff --git a/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp b/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp index 9ca46d2f7..84dd0e262 100644 --- a/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp +++ b/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp @@ -29,151 +29,129 @@ namespace GraphRenderingOps { //============================================================================== -class AudioGraphRenderingOp +struct AudioGraphRenderingOp { -public: - AudioGraphRenderingOp() {} - virtual ~AudioGraphRenderingOp() {} + AudioGraphRenderingOp() noexcept {} + virtual ~AudioGraphRenderingOp() {} virtual void perform (AudioSampleBuffer& sharedBufferChans, - const OwnedArray & sharedMidiBuffers, + const OwnedArray& sharedMidiBuffers, const int numSamples) = 0; JUCE_LEAK_DETECTOR (AudioGraphRenderingOp) }; //============================================================================== -class ClearChannelOp : public AudioGraphRenderingOp +struct ClearChannelOp : public AudioGraphRenderingOp { -public: - ClearChannelOp (const int channelNum_) - : channelNum (channelNum_) - {} + ClearChannelOp (const int channel) noexcept : channelNum (channel) {} - void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray &, const int numSamples) + void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray&, const int numSamples) { sharedBufferChans.clear (channelNum, 0, numSamples); } -private: const int channelNum; JUCE_DECLARE_NON_COPYABLE (ClearChannelOp) }; //============================================================================== -class CopyChannelOp : public AudioGraphRenderingOp +struct CopyChannelOp : public AudioGraphRenderingOp { -public: - CopyChannelOp (const int srcChannelNum_, const int dstChannelNum_) - : srcChannelNum (srcChannelNum_), - dstChannelNum (dstChannelNum_) + CopyChannelOp (const int srcChan, const int dstChan) noexcept + : srcChannelNum (srcChan), dstChannelNum (dstChan) {} - void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray &, const int numSamples) + void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray&, const int numSamples) { sharedBufferChans.copyFrom (dstChannelNum, 0, sharedBufferChans, srcChannelNum, 0, numSamples); } -private: const int srcChannelNum, dstChannelNum; JUCE_DECLARE_NON_COPYABLE (CopyChannelOp) }; //============================================================================== -class AddChannelOp : public AudioGraphRenderingOp +struct AddChannelOp : public AudioGraphRenderingOp { -public: - AddChannelOp (const int srcChannelNum_, const int dstChannelNum_) - : srcChannelNum (srcChannelNum_), - dstChannelNum (dstChannelNum_) + AddChannelOp (const int srcChan, const int dstChan) noexcept + : srcChannelNum (srcChan), dstChannelNum (dstChan) {} - void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray &, const int numSamples) + void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray&, const int numSamples) { sharedBufferChans.addFrom (dstChannelNum, 0, sharedBufferChans, srcChannelNum, 0, numSamples); } -private: const int srcChannelNum, dstChannelNum; JUCE_DECLARE_NON_COPYABLE (AddChannelOp) }; //============================================================================== -class ClearMidiBufferOp : public AudioGraphRenderingOp +struct ClearMidiBufferOp : public AudioGraphRenderingOp { -public: - ClearMidiBufferOp (const int bufferNum_) - : bufferNum (bufferNum_) - {} + ClearMidiBufferOp (const int buffer) noexcept : bufferNum (buffer) {} - void perform (AudioSampleBuffer&, const OwnedArray & sharedMidiBuffers, const int) + void perform (AudioSampleBuffer&, const OwnedArray& sharedMidiBuffers, const int) { sharedMidiBuffers.getUnchecked (bufferNum)->clear(); } -private: const int bufferNum; JUCE_DECLARE_NON_COPYABLE (ClearMidiBufferOp) }; //============================================================================== -class CopyMidiBufferOp : public AudioGraphRenderingOp +struct CopyMidiBufferOp : public AudioGraphRenderingOp { -public: - CopyMidiBufferOp (const int srcBufferNum_, const int dstBufferNum_) - : srcBufferNum (srcBufferNum_), - dstBufferNum (dstBufferNum_) + CopyMidiBufferOp (const int srcBuffer, const int dstBuffer) noexcept + : srcBufferNum (srcBuffer), dstBufferNum (dstBuffer) {} - void perform (AudioSampleBuffer&, const OwnedArray & sharedMidiBuffers, const int) + void perform (AudioSampleBuffer&, const OwnedArray& sharedMidiBuffers, const int) { *sharedMidiBuffers.getUnchecked (dstBufferNum) = *sharedMidiBuffers.getUnchecked (srcBufferNum); } -private: const int srcBufferNum, dstBufferNum; JUCE_DECLARE_NON_COPYABLE (CopyMidiBufferOp) }; //============================================================================== -class AddMidiBufferOp : public AudioGraphRenderingOp +struct AddMidiBufferOp : public AudioGraphRenderingOp { -public: - AddMidiBufferOp (const int srcBufferNum_, const int dstBufferNum_) - : srcBufferNum (srcBufferNum_), - dstBufferNum (dstBufferNum_) + AddMidiBufferOp (const int srcBuffer, const int dstBuffer) + : srcBufferNum (srcBuffer), dstBufferNum (dstBuffer) {} - void perform (AudioSampleBuffer&, const OwnedArray & sharedMidiBuffers, const int numSamples) + void perform (AudioSampleBuffer&, const OwnedArray& sharedMidiBuffers, const int numSamples) { sharedMidiBuffers.getUnchecked (dstBufferNum) ->addEvents (*sharedMidiBuffers.getUnchecked (srcBufferNum), 0, numSamples, 0); } -private: const int srcBufferNum, dstBufferNum; JUCE_DECLARE_NON_COPYABLE (AddMidiBufferOp) }; //============================================================================== -class DelayChannelOp : public AudioGraphRenderingOp +struct DelayChannelOp : public AudioGraphRenderingOp { -public: - DelayChannelOp (const int channel_, const int numSamplesDelay_) - : channel (channel_), - bufferSize (numSamplesDelay_ + 1), - readIndex (0), writeIndex (numSamplesDelay_) + DelayChannelOp (const int chan, const int delaySize) + : channel (chan), + bufferSize (delaySize + 1), + readIndex (0), writeIndex (delaySize) { buffer.calloc ((size_t) bufferSize); } - void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray &, const int numSamples) + void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray&, const int numSamples) { float* data = sharedBufferChans.getWritePointer (channel, 0); @@ -197,18 +175,17 @@ private: //============================================================================== -class ProcessBufferOp : public AudioGraphRenderingOp +struct ProcessBufferOp : public AudioGraphRenderingOp { -public: - ProcessBufferOp (const AudioProcessorGraph::Node::Ptr& node_, - const Array & audioChannelsToUse_, - const int totalChans_, - const int midiBufferToUse_) - : node (node_), - processor (node_->getProcessor()), - audioChannelsToUse (audioChannelsToUse_), - totalChans (jmax (1, totalChans_)), - midiBufferToUse (midiBufferToUse_) + ProcessBufferOp (const AudioProcessorGraph::Node::Ptr& n, + const Array& audioChannels, + const int totalNumChans, + const int midiBuffer) + : node (n), + processor (n->getProcessor()), + audioChannelsToUse (audioChannels), + totalChans (jmax (1, totalNumChans)), + midiBufferToUse (midiBuffer) { channels.calloc ((size_t) totalChans); @@ -216,7 +193,7 @@ public: audioChannelsToUse.add (0); } - void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray & sharedMidiBuffers, const int numSamples) + void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray& sharedMidiBuffers, const int numSamples) { for (int i = totalChans; --i >= 0;) channels[i] = sharedBufferChans.getWritePointer (audioChannelsToUse.getUnchecked (i), 0); @@ -232,8 +209,8 @@ public: private: Array audioChannelsToUse; HeapBlock channels; - int totalChans; - int midiBufferToUse; + const int totalChans; + const int midiBufferToUse; JUCE_DECLARE_NON_COPYABLE (ProcessBufferOp) }; @@ -242,15 +219,13 @@ private: /** Used to calculate the correct sequence of rendering ops needed, based on the best re-use of shared buffers at each stage. */ -class RenderingOpSequenceCalculator +struct RenderingOpSequenceCalculator { -public: - //============================================================================== - RenderingOpSequenceCalculator (AudioProcessorGraph& graph_, - const Array& orderedNodes_, + RenderingOpSequenceCalculator (AudioProcessorGraph& g, + const Array& nodes, Array& renderingOps) - : graph (graph_), - orderedNodes (orderedNodes_), + : graph (g), + orderedNodes (nodes), totalLatency (0) { nodeIds.add ((uint32) zeroNodeID); // first buffer is read-only zeros @@ -260,34 +235,32 @@ public: for (int i = 0; i < orderedNodes.size(); ++i) { - createRenderingOpsForNode ((AudioProcessorGraph::Node*) orderedNodes.getUnchecked(i), - renderingOps, i); - + createRenderingOpsForNode (*orderedNodes.getUnchecked(i), renderingOps, i); markAnyUnusedBuffersAsFree (i); } graph.setLatencySamples (totalLatency); } - int getNumBuffersNeeded() const { return nodeIds.size(); } - int getNumMidiBuffersNeeded() const { return midiNodeIds.size(); } + int getNumBuffersNeeded() const noexcept { return nodeIds.size(); } + int getNumMidiBuffersNeeded() const noexcept { return midiNodeIds.size(); } private: //============================================================================== AudioProcessorGraph& graph; - const Array& orderedNodes; - Array channels; - Array nodeIds, midiNodeIds; + const Array& orderedNodes; + Array channels; + Array nodeIds, midiNodeIds; enum { freeNodeID = 0xffffffff, zeroNodeID = 0xfffffffe }; - static bool isNodeBusy (uint32 nodeID) noexcept { return nodeID != freeNodeID && nodeID != zeroNodeID; } + static bool isNodeBusy (uint32 nodeID) noexcept { return nodeID != freeNodeID && nodeID != zeroNodeID; } - Array nodeDelayIDs; - Array nodeDelays; + Array nodeDelayIDs; + Array nodeDelays; int totalLatency; - int getNodeDelay (const uint32 nodeID) const { return nodeDelays [nodeDelayIDs.indexOf (nodeID)]; } + int getNodeDelay (const uint32 nodeID) const { return nodeDelays [nodeDelayIDs.indexOf (nodeID)]; } void setNodeDelay (const uint32 nodeID, const int latency) { @@ -320,30 +293,31 @@ private: } //============================================================================== - void createRenderingOpsForNode (AudioProcessorGraph::Node* const node, + void createRenderingOpsForNode (AudioProcessorGraph::Node& node, Array& renderingOps, const int ourRenderingIndex) { - const int numIns = node->getProcessor()->getNumInputChannels(); - const int numOuts = node->getProcessor()->getNumOutputChannels(); + AudioProcessor& processor = *node.getProcessor(); + const int numIns = processor.getNumInputChannels(); + const int numOuts = processor.getNumOutputChannels(); const int totalChans = jmax (numIns, numOuts); - Array audioChannelsToUse; + Array audioChannelsToUse; int midiBufferToUse = -1; - int maxLatency = getInputLatencyForNode (node->nodeId); + int maxLatency = getInputLatencyForNode (node.nodeId); for (int inputChan = 0; inputChan < numIns; ++inputChan) { // get a list of all the inputs to this node - Array sourceNodes; + Array sourceNodes; Array sourceOutputChans; for (int i = graph.getNumConnections(); --i >= 0;) { const AudioProcessorGraph::Connection* const c = graph.getConnection (i); - if (c->destNodeId == node->nodeId && c->destChannelIndex == inputChan) + if (c->destNodeId == node.nodeId && c->destChannelIndex == inputChan) { sourceNodes.add (c->sourceNodeId); sourceOutputChans.add (c->sourceChannelIndex); @@ -493,7 +467,7 @@ private: audioChannelsToUse.add (bufIndex); if (inputChan < numOuts) - markBufferAsContaining (bufIndex, node->nodeId, inputChan); + markBufferAsContaining (bufIndex, node.nodeId, inputChan); } for (int outputChan = numIns; outputChan < numOuts; ++outputChan) @@ -502,17 +476,17 @@ private: jassert (bufIndex != 0); audioChannelsToUse.add (bufIndex); - markBufferAsContaining (bufIndex, node->nodeId, outputChan); + markBufferAsContaining (bufIndex, node.nodeId, outputChan); } // Now the same thing for midi.. - Array midiSourceNodes; + Array midiSourceNodes; for (int i = graph.getNumConnections(); --i >= 0;) { const AudioProcessorGraph::Connection* const c = graph.getConnection (i); - if (c->destNodeId == node->nodeId && c->destChannelIndex == AudioProcessorGraph::midiChannelIndex) + if (c->destNodeId == node.nodeId && c->destChannelIndex == AudioProcessorGraph::midiChannelIndex) midiSourceNodes.add (c->sourceNodeId); } @@ -521,7 +495,7 @@ private: // No midi inputs.. midiBufferToUse = getFreeBuffer (true); // need to pick a buffer even if the processor doesn't use midi - if (node->getProcessor()->acceptsMidi() || node->getProcessor()->producesMidi()) + if (processor.acceptsMidi() || processor.producesMidi()) renderingOps.add (new ClearMidiBufferOp (midiBufferToUse)); } else if (midiSourceNodes.size() == 1) @@ -601,16 +575,16 @@ private: } } - if (node->getProcessor()->producesMidi()) - markBufferAsContaining (midiBufferToUse, node->nodeId, + if (processor.producesMidi()) + markBufferAsContaining (midiBufferToUse, node.nodeId, AudioProcessorGraph::midiChannelIndex); - setNodeDelay (node->nodeId, maxLatency + node->getProcessor()->getLatencySamples()); + setNodeDelay (node.nodeId, maxLatency + processor.getLatencySamples()); if (numOuts == 0) totalLatency = maxLatency; - renderingOps.add (new ProcessBufferOp (node, audioChannelsToUse, + renderingOps.add (new ProcessBufferOp (&node, audioChannelsToUse, totalChans, midiBufferToUse)); } @@ -872,18 +846,16 @@ struct ConnectionSorter } //============================================================================== -AudioProcessorGraph::Connection::Connection (const uint32 sourceNodeId_, const int sourceChannelIndex_, - const uint32 destNodeId_, const int destChannelIndex_) noexcept - : sourceNodeId (sourceNodeId_), sourceChannelIndex (sourceChannelIndex_), - destNodeId (destNodeId_), destChannelIndex (destChannelIndex_) +AudioProcessorGraph::Connection::Connection (const uint32 sourceID, const int sourceChannel, + const uint32 destID, const int destChannel) noexcept + : sourceNodeId (sourceID), sourceChannelIndex (sourceChannel), + destNodeId (destID), destChannelIndex (destChannel) { } //============================================================================== -AudioProcessorGraph::Node::Node (const uint32 nodeId_, AudioProcessor* const processor_) noexcept - : nodeId (nodeId_), - processor (processor_), - isPrepared (false) +AudioProcessorGraph::Node::Node (const uint32 nodeID, AudioProcessor* const p) noexcept + : nodeId (nodeID), processor (p), isPrepared (false) { jassert (processor != nullptr); } @@ -916,7 +888,7 @@ void AudioProcessorGraph::Node::unprepare() void AudioProcessorGraph::Node::setParentGraph (AudioProcessorGraph* const graph) const { if (AudioProcessorGraph::AudioGraphIOProcessor* const ioProc - = dynamic_cast (processor.get())) + = dynamic_cast (processor.get())) ioProc->setParentGraph (graph); } @@ -1213,7 +1185,7 @@ void AudioProcessorGraph::buildRenderingSequence() { MessageManagerLock mml; - Array orderedNodes; + Array orderedNodes; { const GraphRenderingOps::ConnectionLookupTable table (connections); @@ -1298,6 +1270,22 @@ void AudioProcessorGraph::reset() nodes.getUnchecked(i)->getProcessor()->reset(); } +void AudioProcessorGraph::setNonRealtime (bool isNonRealtime) noexcept +{ + const ScopedLock sl (getCallbackLock()); + + for (int i = 0; i < nodes.size(); ++i) + nodes.getUnchecked(i)->getProcessor()->setNonRealtime (isNonRealtime); +} + +void AudioProcessorGraph::setPlayHead (AudioPlayHead* audioPlayHead) +{ + const ScopedLock sl (getCallbackLock()); + + for (int i = 0; i < nodes.size(); ++i) + nodes.getUnchecked(i)->getProcessor()->setPlayHead (audioPlayHead); +} + void AudioProcessorGraph::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) { const int numSamples = buffer.getNumSamples(); @@ -1333,20 +1321,19 @@ const String AudioProcessorGraph::getOutputChannelName (int channelIndex) const return "Output " + String (channelIndex + 1); } -bool AudioProcessorGraph::isInputChannelStereoPair (int /*index*/) const { return true; } -bool AudioProcessorGraph::isOutputChannelStereoPair (int /*index*/) const { return true; } -bool AudioProcessorGraph::silenceInProducesSilenceOut() const { return false; } -double AudioProcessorGraph::getTailLengthSeconds() const { return 0; } -bool AudioProcessorGraph::acceptsMidi() const { return true; } -bool AudioProcessorGraph::producesMidi() const { return true; } -void AudioProcessorGraph::getStateInformation (juce::MemoryBlock& /*destData*/) {} -void AudioProcessorGraph::setStateInformation (const void* /*data*/, int /*sizeInBytes*/) {} +bool AudioProcessorGraph::isInputChannelStereoPair (int) const { return true; } +bool AudioProcessorGraph::isOutputChannelStereoPair (int) const { return true; } +bool AudioProcessorGraph::silenceInProducesSilenceOut() const { return false; } +double AudioProcessorGraph::getTailLengthSeconds() const { return 0; } +bool AudioProcessorGraph::acceptsMidi() const { return true; } +bool AudioProcessorGraph::producesMidi() const { return true; } +void AudioProcessorGraph::getStateInformation (juce::MemoryBlock&) {} +void AudioProcessorGraph::setStateInformation (const void*, int) {} //============================================================================== -AudioProcessorGraph::AudioGraphIOProcessor::AudioGraphIOProcessor (const IODeviceType type_) - : type (type_), - graph (nullptr) +AudioProcessorGraph::AudioGraphIOProcessor::AudioGraphIOProcessor (const IODeviceType deviceType) + : type (deviceType), graph (nullptr) { } @@ -1492,19 +1479,12 @@ bool AudioProcessorGraph::AudioGraphIOProcessor::isOutputChannelStereoPair (int return isInputChannelStereoPair (index); } -bool AudioProcessorGraph::AudioGraphIOProcessor::isInput() const { return type == audioInputNode || type == midiInputNode; } -bool AudioProcessorGraph::AudioGraphIOProcessor::isOutput() const { return type == audioOutputNode || type == midiOutputNode; } +bool AudioProcessorGraph::AudioGraphIOProcessor::isInput() const noexcept { return type == audioInputNode || type == midiInputNode; } +bool AudioProcessorGraph::AudioGraphIOProcessor::isOutput() const noexcept { return type == audioOutputNode || type == midiOutputNode; } bool AudioProcessorGraph::AudioGraphIOProcessor::hasEditor() const { return false; } AudioProcessorEditor* AudioProcessorGraph::AudioGraphIOProcessor::createEditor() { return nullptr; } -int AudioProcessorGraph::AudioGraphIOProcessor::getNumParameters() { return 0; } -const String AudioProcessorGraph::AudioGraphIOProcessor::getParameterName (int) { return String(); } - -float AudioProcessorGraph::AudioGraphIOProcessor::getParameter (int) { return 0.0f; } -const String AudioProcessorGraph::AudioGraphIOProcessor::getParameterText (int) { return String(); } -void AudioProcessorGraph::AudioGraphIOProcessor::setParameter (int, float) { } - int AudioProcessorGraph::AudioGraphIOProcessor::getNumPrograms() { return 0; } int AudioProcessorGraph::AudioGraphIOProcessor::getCurrentProgram() { return 0; } void AudioProcessorGraph::AudioGraphIOProcessor::setCurrentProgram (int) { } @@ -1522,7 +1502,7 @@ void AudioProcessorGraph::AudioGraphIOProcessor::setParentGraph (AudioProcessorG if (graph != nullptr) { setPlayConfigDetails (type == audioOutputNode ? graph->getNumOutputChannels() : 0, - type == audioInputNode ? graph->getNumInputChannels() : 0, + type == audioInputNode ? graph->getNumInputChannels() : 0, getSampleRate(), getBlockSize()); diff --git a/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h b/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h index d94468235..319218f7f 100644 --- a/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h +++ b/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h @@ -80,7 +80,7 @@ public: //============================================================================== /** A convenient typedef for referring to a pointer to a node object. */ - typedef ReferenceCountedObjectPtr Ptr; + typedef ReferenceCountedObjectPtr Ptr; private: //============================================================================== @@ -151,13 +151,13 @@ public: void clear(); /** Returns the number of nodes in the graph. */ - int getNumNodes() const { return nodes.size(); } + int getNumNodes() const noexcept { return nodes.size(); } /** Returns a pointer to one of the nodes in the graph. This will return nullptr if the index is out of range. @see getNodeForId */ - Node* getNode (const int index) const { return nodes [index]; } + Node* getNode (const int index) const noexcept { return nodes [index]; } /** Searches the graph for a node with the given ID number and returns it. If no such node was found, this returns nullptr. @@ -289,54 +289,47 @@ public: //============================================================================== /** Returns the mode of this processor. */ - IODeviceType getType() const { return type; } + IODeviceType getType() const noexcept { return type; } /** Returns the parent graph to which this processor belongs, or nullptr if it hasn't yet been added to one. */ - AudioProcessorGraph* getParentGraph() const { return graph; } + AudioProcessorGraph* getParentGraph() const noexcept { return graph; } /** True if this is an audio or midi input. */ - bool isInput() const; + bool isInput() const noexcept; /** True if this is an audio or midi output. */ - bool isOutput() const; + bool isOutput() const noexcept; //============================================================================== AudioGraphIOProcessor (const IODeviceType type); ~AudioGraphIOProcessor(); - const String getName() const; - void fillInPluginDescription (PluginDescription&) const; - - void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); - void releaseResources(); + const String getName() const override; + void fillInPluginDescription (PluginDescription&) const override; + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock) override; + void releaseResources() override; void processBlock (AudioSampleBuffer&, MidiBuffer&); - const String getInputChannelName (int channelIndex) const; - const String getOutputChannelName (int channelIndex) const; - bool isInputChannelStereoPair (int index) const; - bool isOutputChannelStereoPair (int index) const; - bool silenceInProducesSilenceOut() const; - double getTailLengthSeconds() const; - bool acceptsMidi() const; - bool producesMidi() const; - - bool hasEditor() const; - AudioProcessorEditor* createEditor(); - - int getNumParameters(); - const String getParameterName (int); - float getParameter (int); - const String getParameterText (int); - void setParameter (int, float); - - int getNumPrograms(); - int getCurrentProgram(); - void setCurrentProgram (int); - const String getProgramName (int); - void changeProgramName (int, const String&); - - void getStateInformation (juce::MemoryBlock& destData); - void setStateInformation (const void* data, int sizeInBytes); + const String getInputChannelName (int channelIndex) const override; + const String getOutputChannelName (int channelIndex) const override; + bool isInputChannelStereoPair (int index) const override; + bool isOutputChannelStereoPair (int index) const override; + bool silenceInProducesSilenceOut() const override; + double getTailLengthSeconds() const override; + bool acceptsMidi() const override; + bool producesMidi() const override; + + bool hasEditor() const override; + AudioProcessorEditor* createEditor() override; + + int getNumPrograms() override; + int getCurrentProgram() override; + void setCurrentProgram (int) override; + const String getProgramName (int) override; + void changeProgramName (int, const String&) override; + + void getStateInformation (juce::MemoryBlock& destData) override; + void setStateInformation (const void* data, int sizeInBytes) override; /** @internal */ void setParentGraph (AudioProcessorGraph*); @@ -349,42 +342,34 @@ public: }; //============================================================================== - // AudioProcessor methods: - - const String getName() const; - - void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); - void releaseResources(); - void processBlock (AudioSampleBuffer&, MidiBuffer&); - void reset(); - - const String getInputChannelName (int channelIndex) const; - const String getOutputChannelName (int channelIndex) const; - bool isInputChannelStereoPair (int index) const; - bool isOutputChannelStereoPair (int index) const; - bool silenceInProducesSilenceOut() const; - double getTailLengthSeconds() const; - - bool acceptsMidi() const; - bool producesMidi() const; - - bool hasEditor() const { return false; } - AudioProcessorEditor* createEditor() { return nullptr; } - - int getNumParameters() { return 0; } - const String getParameterName (int) { return String(); } - float getParameter (int) { return 0; } - const String getParameterText (int) { return String(); } - void setParameter (int, float) { } - - int getNumPrograms() { return 0; } - int getCurrentProgram() { return 0; } - void setCurrentProgram (int) { } - const String getProgramName (int) { return String(); } - void changeProgramName (int, const String&) { } - - void getStateInformation (juce::MemoryBlock&); - void setStateInformation (const void* data, int sizeInBytes); + const String getName() const override; + void prepareToPlay (double, int) override; + void releaseResources() override; + void processBlock (AudioSampleBuffer&, MidiBuffer&) override; + + void reset() override; + void setNonRealtime (bool) noexcept override; + void setPlayHead (AudioPlayHead*) override; + + const String getInputChannelName (int) const override; + const String getOutputChannelName (int) const override; + bool isInputChannelStereoPair (int) const override; + bool isOutputChannelStereoPair (int) const override; + + bool silenceInProducesSilenceOut() const override; + double getTailLengthSeconds() const override; + bool acceptsMidi() const override; + bool producesMidi() const override; + + bool hasEditor() const override { return false; } + AudioProcessorEditor* createEditor() override { return nullptr; } + int getNumPrograms() override { return 0; } + int getCurrentProgram() override { return 0; } + void setCurrentProgram (int) override { } + const String getProgramName (int) override { return String(); } + void changeProgramName (int, const String&) override { } + void getStateInformation (juce::MemoryBlock&) override; + void setStateInformation (const void* data, int sizeInBytes) override; private: //============================================================================== diff --git a/source/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp b/source/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp index c94411d7b..196e9ac9c 100644 --- a/source/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp +++ b/source/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp @@ -67,7 +67,7 @@ public: if (paramHasChanged) { refresh(); - startTimer (1000 / 50); + startTimerHz (50); } else { diff --git a/source/modules/juce_core/containers/juce_Array.h b/source/modules/juce_core/containers/juce_Array.h index 8e9cf3f9b..b729c99d2 100644 --- a/source/modules/juce_core/containers/juce_Array.h +++ b/source/modules/juce_core/containers/juce_Array.h @@ -96,8 +96,7 @@ public: @param values the array to copy from */ template - explicit Array (const TypeToCreateFrom* values) - : numUsed (0) + explicit Array (const TypeToCreateFrom* values) : numUsed (0) { while (*values != TypeToCreateFrom()) add (*values++); @@ -109,8 +108,7 @@ public: @param numValues the number of values in the array */ template - Array (const TypeToCreateFrom* values, int numValues) - : numUsed (numValues) + Array (const TypeToCreateFrom* values, int numValues) : numUsed (numValues) { data.setAllocatedSize (numValues); @@ -118,6 +116,14 @@ public: new (data.elements + i) ElementType (values[i]); } + #if JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS + template + Array (const std::initializer_list& items) : numUsed (0) + { + addArray (items); + } + #endif + /** Destructor. */ ~Array() { @@ -474,7 +480,11 @@ public: numUsed += numberOfTimesToInsertIt; while (--numberOfTimesToInsertIt >= 0) - new (insertPos++) ElementType (newElement); + { + new (insertPos) ElementType (newElement); + ++insertPos; // NB: this increment is done separately from the + // new statement to avoid a compiler bug in VS2014 + } } } @@ -600,6 +610,21 @@ public: } } + #if JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS + template + void addArray (const std::initializer_list& items) + { + const ScopedLockType lock (getLock()); + data.ensureAllocatedSize (numUsed + (int) items.size()); + + for (auto& item : items) + { + new (data.elements + numUsed) ElementType (item); + ++numUsed; + } + } + #endif + /** Adds elements from a null-terminated array of pointers to the end of this array. @param elementsToAdd an array of pointers to some kind of object from which elements diff --git a/source/modules/juce_core/containers/juce_DynamicObject.cpp b/source/modules/juce_core/containers/juce_DynamicObject.cpp index 38f5c5f08..3351f19b8 100644 --- a/source/modules/juce_core/containers/juce_DynamicObject.cpp +++ b/source/modules/juce_core/containers/juce_DynamicObject.cpp @@ -45,7 +45,7 @@ bool DynamicObject::hasProperty (const Identifier& propertyName) const return v != nullptr && ! v->isMethod(); } -var DynamicObject::getProperty (const Identifier& propertyName) const +const var& DynamicObject::getProperty (const Identifier& propertyName) const { return properties [propertyName]; } diff --git a/source/modules/juce_core/containers/juce_DynamicObject.h b/source/modules/juce_core/containers/juce_DynamicObject.h index 5c624a4d9..598df6ed6 100644 --- a/source/modules/juce_core/containers/juce_DynamicObject.h +++ b/source/modules/juce_core/containers/juce_DynamicObject.h @@ -60,7 +60,7 @@ public: /** Returns a named property. This returns var::null if no such property exists. */ - virtual var getProperty (const Identifier& propertyName) const; + virtual const var& getProperty (const Identifier& propertyName) const; /** Sets a named property. */ virtual void setProperty (const Identifier& propertyName, const var& newValue); diff --git a/source/modules/juce_core/containers/juce_ReferenceCountedArray.h b/source/modules/juce_core/containers/juce_ReferenceCountedArray.h index d33343d95..6b02c3e3e 100644 --- a/source/modules/juce_core/containers/juce_ReferenceCountedArray.h +++ b/source/modules/juce_core/containers/juce_ReferenceCountedArray.h @@ -38,7 +38,7 @@ The template parameter specifies the class of the object you want to point to - the easiest way to make a class reference-countable is to simply make it inherit from ReferenceCountedObject or SingleThreadedReferenceCountedObject, but if you need to, you can roll your own reference-countable - class by implementing a set of mathods called incReferenceCount(), decReferenceCount(), and + class by implementing a set of methods called incReferenceCount(), decReferenceCount(), and decReferenceCountWithoutDeleting(). See ReferenceCountedObject for examples of how these methods should behave. diff --git a/source/modules/juce_core/containers/juce_Variant.cpp b/source/modules/juce_core/containers/juce_Variant.cpp index db3f2f566..833d7927a 100644 --- a/source/modules/juce_core/containers/juce_Variant.cpp +++ b/source/modules/juce_core/containers/juce_Variant.cpp @@ -174,7 +174,7 @@ public: int toInt (const ValueUnion& data) const noexcept override { return (int) data.doubleValue; }; int64 toInt64 (const ValueUnion& data) const noexcept override { return (int64) data.doubleValue; }; double toDouble (const ValueUnion& data) const noexcept override { return data.doubleValue; } - String toString (const ValueUnion& data) const override { return String (data.doubleValue); } + String toString (const ValueUnion& data) const override { return String (data.doubleValue, 20); } bool toBool (const ValueUnion& data) const noexcept override { return data.doubleValue != 0; } bool isDouble() const noexcept override { return true; } @@ -253,8 +253,8 @@ public: } private: - static inline const String* getString (const ValueUnion& data) noexcept { return reinterpret_cast (data.stringValue); } - static inline String* getString (ValueUnion& data) noexcept { return reinterpret_cast (data.stringValue); } + static inline const String* getString (const ValueUnion& data) noexcept { return reinterpret_cast (data.stringValue); } + static inline String* getString (ValueUnion& data) noexcept { return reinterpret_cast (data.stringValue); } }; //============================================================================== @@ -487,7 +487,7 @@ var::operator String() const { return type->toString ReferenceCountedObject* var::getObject() const noexcept { return type->toObject (value); } Array* var::getArray() const noexcept { return type->toArray (value); } MemoryBlock* var::getBinaryData() const noexcept { return type->toBinary (value); } -DynamicObject* var::getDynamicObject() const noexcept { return dynamic_cast (getObject()); } +DynamicObject* var::getDynamicObject() const noexcept { return dynamic_cast (getObject()); } //============================================================================== void var::swapWith (var& other) noexcept @@ -576,15 +576,15 @@ var var::clone() const noexcept } //============================================================================== -var var::operator[] (const Identifier propertyName) const +const var& var::operator[] (Identifier propertyName) const { if (DynamicObject* const o = getDynamicObject()) return o->getProperty (propertyName); - return var(); + return var::null; } -var var::operator[] (const char* const propertyName) const +const var& var::operator[] (const char* const propertyName) const { return operator[] (Identifier (propertyName)); } diff --git a/source/modules/juce_core/containers/juce_Variant.h b/source/modules/juce_core/containers/juce_Variant.h index 41a4884c1..1e0f03cf5 100644 --- a/source/modules/juce_core/containers/juce_Variant.h +++ b/source/modules/juce_core/containers/juce_Variant.h @@ -242,9 +242,9 @@ public: //============================================================================== /** If this variant is an object, this returns one of its properties. */ - var operator[] (Identifier propertyName) const; + const var& operator[] (Identifier propertyName) const; /** If this variant is an object, this returns one of its properties. */ - var operator[] (const char* propertyName) const; + const var& operator[] (const char* propertyName) const; /** If this variant is an object, this returns one of its properties, or a default fallback value if the property is not set. */ var getProperty (Identifier propertyName, const var& defaultReturnValue) const; diff --git a/source/modules/juce_core/files/juce_DirectoryIterator.cpp b/source/modules/juce_core/files/juce_DirectoryIterator.cpp index 7d6dd4b13..f2e09a61b 100644 --- a/source/modules/juce_core/files/juce_DirectoryIterator.cpp +++ b/source/modules/juce_core/files/juce_DirectoryIterator.cpp @@ -73,64 +73,71 @@ bool DirectoryIterator::next() bool DirectoryIterator::next (bool* const isDirResult, bool* const isHiddenResult, int64* const fileSize, Time* const modTime, Time* const creationTime, bool* const isReadOnly) { - hasBeenAdvanced = true; - - if (subIterator != nullptr) + for (;;) { - if (subIterator->next (isDirResult, isHiddenResult, fileSize, modTime, creationTime, isReadOnly)) - return true; - - subIterator = nullptr; - } - - String filename; - bool isDirectory, isHidden = false; + hasBeenAdvanced = true; - while (fileFinder.next (filename, &isDirectory, - (isHiddenResult != nullptr || (whatToLookFor & File::ignoreHiddenFiles) != 0) ? &isHidden : nullptr, - fileSize, modTime, creationTime, isReadOnly)) - { - ++index; - - if (! filename.containsOnly (".")) + if (subIterator != nullptr) { - bool matches = false; - - if (isDirectory) - { - if (isRecursive && ((whatToLookFor & File::ignoreHiddenFiles) == 0 || ! isHidden)) - subIterator = new DirectoryIterator (File::createFileWithoutCheckingPath (path + filename), - true, wildCard, whatToLookFor); + if (subIterator->next (isDirResult, isHiddenResult, fileSize, modTime, creationTime, isReadOnly)) + return true; - matches = (whatToLookFor & File::findDirectories) != 0; - } - else - { - matches = (whatToLookFor & File::findFiles) != 0; - } + subIterator = nullptr; + } - // if we're not relying on the OS iterator to do the wildcard match, do it now.. - if (matches && (isRecursive || wildCards.size() > 1)) - matches = fileMatches (wildCards, filename); + String filename; + bool isDirectory, isHidden = false, shouldContinue = false; - if (matches && (whatToLookFor & File::ignoreHiddenFiles) != 0) - matches = ! isHidden; + while (fileFinder.next (filename, &isDirectory, + (isHiddenResult != nullptr || (whatToLookFor & File::ignoreHiddenFiles) != 0) ? &isHidden : nullptr, + fileSize, modTime, creationTime, isReadOnly)) + { + ++index; - if (matches) + if (! filename.containsOnly (".")) { - currentFile = File::createFileWithoutCheckingPath (path + filename); - if (isHiddenResult != nullptr) *isHiddenResult = isHidden; - if (isDirResult != nullptr) *isDirResult = isDirectory; - - return true; + bool matches = false; + + if (isDirectory) + { + if (isRecursive && ((whatToLookFor & File::ignoreHiddenFiles) == 0 || ! isHidden)) + subIterator = new DirectoryIterator (File::createFileWithoutCheckingPath (path + filename), + true, wildCard, whatToLookFor); + + matches = (whatToLookFor & File::findDirectories) != 0; + } + else + { + matches = (whatToLookFor & File::findFiles) != 0; + } + + // if we're not relying on the OS iterator to do the wildcard match, do it now.. + if (matches && (isRecursive || wildCards.size() > 1)) + matches = fileMatches (wildCards, filename); + + if (matches && (whatToLookFor & File::ignoreHiddenFiles) != 0) + matches = ! isHidden; + + if (matches) + { + currentFile = File::createFileWithoutCheckingPath (path + filename); + if (isHiddenResult != nullptr) *isHiddenResult = isHidden; + if (isDirResult != nullptr) *isDirResult = isDirectory; + + return true; + } + + if (subIterator != nullptr) + { + shouldContinue = true; + break; + } } - - if (subIterator != nullptr) - return next (isDirResult, isHiddenResult, fileSize, modTime, creationTime, isReadOnly); } - } - return false; + if (! shouldContinue) + return false; + } } const File& DirectoryIterator::getFile() const diff --git a/source/modules/juce_core/files/juce_File.cpp b/source/modules/juce_core/files/juce_File.cpp index d5c520a09..19d724b0a 100644 --- a/source/modules/juce_core/files/juce_File.cpp +++ b/source/modules/juce_core/files/juce_File.cpp @@ -219,6 +219,11 @@ bool File::setReadOnly (const bool shouldBeReadOnly, return setFileReadOnlyInternal (shouldBeReadOnly) && worked; } +bool File::setExecutePermission (bool shouldBeExecutable) const +{ + return setFileExecutableInternal (shouldBeExecutable); +} + bool File::deleteRecursively() const { bool worked = true; diff --git a/source/modules/juce_core/files/juce_File.h b/source/modules/juce_core/files/juce_File.h index 57d970fff..5c9a1cd18 100644 --- a/source/modules/juce_core/files/juce_File.h +++ b/source/modules/juce_core/files/juce_File.h @@ -348,6 +348,13 @@ public: bool setReadOnly (bool shouldBeReadOnly, bool applyRecursively = false) const; + /** Changes the execute-permissions of a file. + + @param shouldBeExecutable whether to add or remove execute-permission + @returns true if it manages to change the file's permissions. + */ + bool setExecutePermission (bool shouldBeExecutable) const; + /** Returns true if this file is a hidden or system file. The criteria for deciding whether a file is hidden are platform-dependent. */ @@ -486,6 +493,9 @@ public: Note that the destination file isn't the directory to put it in, it's the actual filename that you want the new file to have. + Also note that on some OSes (e.g. Windows), moving files between different + volumes may not be possible. + @returns true if the operation succeeds */ bool moveFileTo (const File& targetLocation) const; @@ -968,6 +978,7 @@ private: bool setFileTimesInternal (int64 m, int64 a, int64 c) const; void getFileTimesInternal (int64& m, int64& a, int64& c) const; bool setFileReadOnlyInternal (bool) const; + bool setFileExecutableInternal (bool) const; }; #endif // JUCE_FILE_H_INCLUDED diff --git a/source/modules/juce_core/javascript/juce_Javascript.cpp b/source/modules/juce_core/javascript/juce_Javascript.cpp index e78bb3711..c2e6e9649 100644 --- a/source/modules/juce_core/javascript/juce_Javascript.cpp +++ b/source/modules/juce_core/javascript/juce_Javascript.cpp @@ -1453,12 +1453,12 @@ struct JavascriptEngine::RootObject : public DynamicObject ObjectClass() { setMethod ("dump", dump); - setMethod ("clone", clone); + setMethod ("clone", cloneFn); } static Identifier getClassName() { static const Identifier i ("Object"); return i; } static var dump (Args a) { DBG (JSON::toString (a.thisObject)); (void) a; return var::undefined(); } - static var clone (Args a) { return a.thisObject.clone(); } + static var cloneFn (Args a) { return a.thisObject.clone(); } }; //============================================================================== diff --git a/source/modules/juce_core/juce_core.cpp b/source/modules/juce_core/juce_core.cpp index 7cfc66453..de5c8e562 100644 --- a/source/modules/juce_core/juce_core.cpp +++ b/source/modules/juce_core/juce_core.cpp @@ -45,9 +45,9 @@ #include #include -#include #if ! JUCE_ANDROID + #include #include #endif diff --git a/source/modules/juce_core/juce_core.h b/source/modules/juce_core/juce_core.h index 323529b70..120773c9a 100644 --- a/source/modules/juce_core/juce_core.h +++ b/source/modules/juce_core/juce_core.h @@ -48,6 +48,15 @@ #endif #endif +#ifdef _MSC_VER + #pragma warning (push) + // Disable warnings for long class names, padding, and undefined preprocessor definitions. + #pragma warning (disable: 4251 4786 4668 4820) + #ifdef __INTEL_COMPILER + #pragma warning (disable: 1125) + #endif +#endif + //============================================================================== #include "system/juce_TargetPlatform.h" @@ -131,14 +140,6 @@ //============================================================================= //============================================================================= -#if JUCE_MSVC - #pragma warning (disable: 4251) // (DLL build warning, must be disabled before pushing the warning state) - #pragma warning (push) - #pragma warning (disable: 4786) // (long class name warning) - #ifdef __INTEL_COMPILER - #pragma warning (disable: 1125) - #endif -#endif #include "system/juce_StandardHeader.h" @@ -244,7 +245,6 @@ extern JUCE_API void JUCE_CALLTYPE logAssertion (const char* file, int line) noe #include "maths/juce_Random.h" #include "misc/juce_Uuid.h" #include "misc/juce_WindowsRegistry.h" -#include "system/juce_PlatformDefs.h" #include "system/juce_SystemStats.h" #include "threads/juce_ChildProcess.h" #include "threads/juce_DynamicLibrary.h" diff --git a/source/modules/juce_core/logging/juce_FileLogger.cpp b/source/modules/juce_core/logging/juce_FileLogger.cpp index 8cadfda8b..1b8f302f2 100644 --- a/source/modules/juce_core/logging/juce_FileLogger.cpp +++ b/source/modules/juce_core/logging/juce_FileLogger.cpp @@ -32,7 +32,7 @@ FileLogger::FileLogger (const File& file, : logFile (file) { if (maxInitialFileSizeBytes >= 0) - trimFileSize (maxInitialFileSizeBytes); + trimFileSize (logFile, maxInitialFileSizeBytes); if (! file.exists()) file.create(); // (to create the parent directories) @@ -57,23 +57,23 @@ void FileLogger::logMessage (const String& message) out << message << newLine; } -void FileLogger::trimFileSize (int64 maxFileSizeBytes) const +void FileLogger::trimFileSize (const File& file, int64 maxFileSizeBytes) { if (maxFileSizeBytes <= 0) { - logFile.deleteFile(); + file.deleteFile(); } else { - const int64 fileSize = logFile.getSize(); + const int64 fileSize = file.getSize(); if (fileSize > maxFileSizeBytes) { - TemporaryFile tempFile (logFile); + TemporaryFile tempFile (file); { FileOutputStream out (tempFile.getFile()); - FileInputStream in (logFile); + FileInputStream in (file); if (! (out.openedOk() && in.openedOk())) return; diff --git a/source/modules/juce_core/logging/juce_FileLogger.h b/source/modules/juce_core/logging/juce_FileLogger.h index 6be1668cb..09301b863 100644 --- a/source/modules/juce_core/logging/juce_FileLogger.h +++ b/source/modules/juce_core/logging/juce_FileLogger.h @@ -121,13 +121,17 @@ public: // (implementation of the Logger virtual method) void logMessage (const String&); + //============================================================================== + /** This is a utility function which removes lines from the start of a text + file to make sure that its total size is below the given size. + */ + static void trimFileSize (const File& file, int64 maxFileSize); + private: //============================================================================== File logFile; CriticalSection logLock; - void trimFileSize (int64 maxFileSizeBytes) const; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileLogger) }; diff --git a/source/modules/juce_core/maths/juce_Expression.cpp b/source/modules/juce_core/maths/juce_Expression.cpp index a18effa04..632b90a78 100644 --- a/source/modules/juce_core/maths/juce_Expression.cpp +++ b/source/modules/juce_core/maths/juce_Expression.cpp @@ -1162,9 +1162,9 @@ double Expression::Scope::evaluateFunction (const String& functionName, const do if (numParams == 1) { - if (functionName == "sin") return sin (parameters[0]); - if (functionName == "cos") return cos (parameters[0]); - if (functionName == "tan") return tan (parameters[0]); + if (functionName == "sin") return std::sin (parameters[0]); + if (functionName == "cos") return std::cos (parameters[0]); + if (functionName == "tan") return std::tan (parameters[0]); if (functionName == "abs") return std::abs (parameters[0]); } } diff --git a/source/modules/juce_core/maths/juce_MathsFunctions.h b/source/modules/juce_core/maths/juce_MathsFunctions.h index d2d02b900..9e0781e11 100644 --- a/source/modules/juce_core/maths/juce_MathsFunctions.h +++ b/source/modules/juce_core/maths/juce_MathsFunctions.h @@ -119,6 +119,22 @@ inline Type jmin (const Type a, const Type b, const Type c) template inline Type jmin (const Type a, const Type b, const Type c, const Type d) { return jmin (a, jmin (b, c, d)); } +/** Remaps a normalised value (between 0 and 1) to a target range. + This effectively returns (targetRangeMin + value0To1 * (targetRangeMax - targetRangeMin)) +*/ +template +static Type jmap (Type value0To1, Type targetRangeMin, Type targetRangeMax) +{ + return targetRangeMin + value0To1 * (targetRangeMax - targetRangeMin); +} + +/** Remaps a value from a source range to a target range. */ +template +static Type jmap (Type sourceValue, Type sourceRangeMin, Type sourceRangeMax, Type targetRangeMin, Type targetRangeMax) +{ + return targetRangeMin + ((targetRangeMax - targetRangeMin) * (sourceValue - sourceRangeMin)) / (sourceRangeMax - sourceRangeMin); +} + /** Scans an array of values, returning the minimum value that it contains. */ template const Type findMinimum (const Type* data, int numValues) diff --git a/source/modules/juce_core/maths/juce_NormalisableRange.h b/source/modules/juce_core/maths/juce_NormalisableRange.h index 800c61630..56424a2a2 100644 --- a/source/modules/juce_core/maths/juce_NormalisableRange.h +++ b/source/modules/juce_core/maths/juce_NormalisableRange.h @@ -104,7 +104,7 @@ public: ValueType proportion = (v - start) / (end - start); if (skew != static_cast (1)) - proportion = pow (proportion, skew); + proportion = std::pow (proportion, skew); return proportion; } @@ -115,7 +115,7 @@ public: ValueType convertFrom0to1 (ValueType proportion) const noexcept { if (skew != static_cast (1) && proportion > ValueType()) - proportion = exp (log (proportion) / skew); + proportion = std::exp (std::log (proportion) / skew); return start + (end - start) * proportion; } diff --git a/source/modules/juce_core/memory/juce_ReferenceCountedObject.h b/source/modules/juce_core/memory/juce_ReferenceCountedObject.h index 6464d8572..36ac5c3a7 100644 --- a/source/modules/juce_core/memory/juce_ReferenceCountedObject.h +++ b/source/modules/juce_core/memory/juce_ReferenceCountedObject.h @@ -208,7 +208,7 @@ private: The template parameter specifies the class of the object you want to point to - the easiest way to make a class reference-countable is to simply make it inherit from ReferenceCountedObject or SingleThreadedReferenceCountedObject, but if you need to, you can roll your own reference-countable - class by implementing a set of mathods called incReferenceCount(), decReferenceCount(), and + class by implementing a set of methods called incReferenceCount(), decReferenceCount(), and decReferenceCountWithoutDeleting(). See ReferenceCountedObject for examples of how these methods should behave. diff --git a/source/modules/juce_core/native/java/JuceAppActivity.java b/source/modules/juce_core/native/java/JuceAppActivity.java index 882d0e0c1..5c079df6c 100644 --- a/source/modules/juce_core/native/java/JuceAppActivity.java +++ b/source/modules/juce_core/native/java/JuceAppActivity.java @@ -42,10 +42,8 @@ import android.opengl.*; import android.text.ClipboardManager; import android.text.InputType; import android.util.DisplayMetrics; -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import android.util.Log; +import java.io.*; import java.net.URL; import java.net.HttpURLConnection; import javax.microedition.khronos.egl.EGLConfig; @@ -55,7 +53,7 @@ import android.media.MediaScannerConnection; import android.media.MediaScannerConnection.MediaScannerConnectionClient; //============================================================================== -public final class JuceAppActivity extends Activity +public class JuceAppActivity extends Activity { //============================================================================== static @@ -64,7 +62,7 @@ public final class JuceAppActivity extends Activity } @Override - public final void onCreate (Bundle savedInstanceState) + public void onCreate (Bundle savedInstanceState) { super.onCreate (savedInstanceState); @@ -75,14 +73,16 @@ public final class JuceAppActivity extends Activity } @Override - protected final void onDestroy() + protected void onDestroy() { quitApp(); super.onDestroy(); + + clearDataCache(); } @Override - protected final void onPause() + protected void onPause() { if (viewHolder != null) viewHolder.onPause(); @@ -92,7 +92,7 @@ public final class JuceAppActivity extends Activity } @Override - protected final void onResume() + protected void onResume() { super.onResume(); @@ -157,6 +157,14 @@ public final class JuceAppActivity extends Activity group.removeView (view); } + public final void deleteOpenGLView (OpenGLView view) + { + ViewGroup group = (ViewGroup) (view.getParent()); + + if (group != null) + group.removeView (view); + } + final class ViewHolder extends ViewGroup { public ViewHolder (Context context) @@ -666,38 +674,103 @@ public final class JuceAppActivity extends Activity public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData, String headers, int timeOutMs, int[] statusCode, - StringBuffer responseHeaders) + StringBuffer responseHeaders, + int numRedirectsToFollow) { - try + // timeout parameter of zero for HttpUrlConnection is a blocking connect (negative value for juce::URL) + if (timeOutMs < 0) + timeOutMs = 0; + else if (timeOutMs == 0) + timeOutMs = 30000; + + // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines. + // So convert headers string to an array, with an element for each line + String headerLines[] = headers.split("\\n"); + + for (;;) { - HttpURLConnection connection = (HttpURLConnection) (new URL(address) - .openConnection()); - if (connection != null) + try { - try + HttpURLConnection connection = (HttpURLConnection) (new URL(address).openConnection()); + + if (connection != null) { - if (isPost) + try { - connection.setRequestMethod("POST"); - connection.setConnectTimeout(timeOutMs); - connection.setDoOutput(true); - connection.setChunkedStreamingMode(0); - OutputStream out = connection.getOutputStream(); - out.write(postData); - out.flush(); - } + connection.setInstanceFollowRedirects (false); + connection.setConnectTimeout (timeOutMs); + connection.setReadTimeout (timeOutMs); - return new HTTPStream (connection, statusCode, responseHeaders); - } - catch (Throwable e) - { - connection.disconnect(); + // Set request headers + for (int i = 0; i < headerLines.length; ++i) + { + int pos = headerLines[i].indexOf (":"); + + if (pos > 0 && pos < headerLines[i].length()) + { + String field = headerLines[i].substring (0, pos); + String value = headerLines[i].substring (pos + 1); + + if (value.length() > 0) + connection.setRequestProperty (field, value); + } + } + + if (isPost) + { + connection.setRequestMethod ("POST"); + connection.setDoOutput (true); + + if (postData != null) + { + OutputStream out = connection.getOutputStream(); + out.write(postData); + out.flush(); + } + } + + HTTPStream httpStream = new HTTPStream (connection, statusCode, responseHeaders); + + // Process redirect & continue as necessary + int status = statusCode[0]; + + if (--numRedirectsToFollow >= 0 + && (status == 301 || status == 302 || status == 303 || status == 307)) + { + // Assumes only one occurrence of "Location" + int pos1 = responseHeaders.indexOf ("Location:") + 10; + int pos2 = responseHeaders.indexOf ("\n", pos1); + + if (pos2 > pos1) + { + String newLocation = responseHeaders.substring(pos1, pos2); + // Handle newLocation whether it's absolute or relative + URL baseUrl = new URL (address); + URL newUrl = new URL (baseUrl, newLocation); + String transformedNewLocation = newUrl.toString(); + + if (transformedNewLocation != address) + { + address = transformedNewLocation; + // Clear responseHeaders before next iteration + responseHeaders.delete (0, responseHeaders.length()); + continue; + } + } + } + + return httpStream; + } + catch (Throwable e) + { + connection.disconnect(); + } } } - } - catch (Throwable e) {} + catch (Throwable e) {} - return null; + return null; + } } public final void launchURL (String url) @@ -743,4 +816,85 @@ public final class JuceAppActivity extends Activity { new SingleMediaScanner (this, filename); } + + public final Typeface getTypeFaceFromAsset (String assetName) + { + try + { + return Typeface.createFromAsset (this.getResources().getAssets(), assetName); + } + catch (Throwable e) {} + + return null; + } + + final protected static char[] hexArray = "0123456789ABCDEF".toCharArray(); + + public static String bytesToHex (byte[] bytes) + { + char[] hexChars = new char[bytes.length * 2]; + + for (int j = 0; j < bytes.length; ++j) + { + int v = bytes[j] & 0xff; + hexChars[j * 2] = hexArray[v >>> 4]; + hexChars[j * 2 + 1] = hexArray[v & 0x0f]; + } + + return new String (hexChars); + } + + final private java.util.Map dataCache = new java.util.HashMap(); + + synchronized private final File getDataCacheFile (byte[] data) + { + try + { + java.security.MessageDigest digest = java.security.MessageDigest.getInstance ("MD5"); + digest.update (data); + + String key = bytesToHex (digest.digest()); + + if (dataCache.containsKey (key)) + return (File) dataCache.get (key); + + File f = new File (this.getCacheDir(), "bindata_" + key); + f.delete(); + FileOutputStream os = new FileOutputStream (f); + os.write (data, 0, data.length); + dataCache.put (key, f); + return f; + } + catch (Throwable e) {} + + return null; + } + + private final void clearDataCache() + { + java.util.Iterator it = dataCache.values().iterator(); + + while (it.hasNext()) + { + File f = (File) it.next(); + f.delete(); + } + } + + public final Typeface getTypeFaceFromByteArray (byte[] data) + { + try + { + File f = getDataCacheFile (data); + + if (f != null) + return Typeface.createFromFile (f); + } + catch (Exception e) + { + Log.e ("JUCE", e.toString()); + } + + return null; + } } diff --git a/source/modules/juce_core/native/juce_BasicNativeHeaders.h b/source/modules/juce_core/native/juce_BasicNativeHeaders.h index 94c47ab93..55c0c8c30 100644 --- a/source/modules/juce_core/native/juce_BasicNativeHeaders.h +++ b/source/modules/juce_core/native/juce_BasicNativeHeaders.h @@ -97,7 +97,6 @@ #define _WIN32_IE 0x0500 #endif - #include #include #include #include diff --git a/source/modules/juce_core/native/juce_android_JNIHelpers.h b/source/modules/juce_core/native/juce_android_JNIHelpers.h index 8e8b07d3a..0c35c63dd 100644 --- a/source/modules/juce_core/native/juce_android_JNIHelpers.h +++ b/source/modules/juce_core/native/juce_android_JNIHelpers.h @@ -290,13 +290,23 @@ public: if (android.activity != nullptr) { jvm->DetachCurrentThread(); + removeCurrentThreadFromCache(); + } + } + + void removeCurrentThreadFromCache() + { + const pthread_t thisThread = pthread_self(); - const pthread_t thisThread = pthread_self(); + SpinLock::ScopedLockType sl (addRemoveLock); - SpinLock::ScopedLockType sl (addRemoveLock); - for (int i = 0; i < maxThreads; ++i) - if (threads[i] == thisThread) - threads[i] = 0; + for (int i = 0; i < maxThreads; ++i) + { + if (threads[i] == thisThread) + { + threads[i] = 0; + envs[i] = nullptr; + } } } @@ -372,19 +382,22 @@ struct AndroidThreadScope #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ METHOD (createNewView, "createNewView", "(ZJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;") \ METHOD (deleteView, "deleteView", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;)V") \ + METHOD (deleteOpenGLView, "deleteOpenGLView", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$OpenGLView;)V") \ METHOD (postMessage, "postMessage", "(J)V") \ METHOD (finish, "finish", "()V") \ METHOD (getClipboardContent, "getClipboardContent", "()Ljava/lang/String;") \ METHOD (setClipboardContent, "setClipboardContent", "(Ljava/lang/String;)V") \ METHOD (excludeClipRegion, "excludeClipRegion", "(Landroid/graphics/Canvas;FFFF)V") \ METHOD (renderGlyph, "renderGlyph", "(CLandroid/graphics/Paint;Landroid/graphics/Matrix;Landroid/graphics/Rect;)[I") \ - STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \ + STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;I)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \ METHOD (launchURL, "launchURL", "(Ljava/lang/String;)V") \ METHOD (showMessageBox, "showMessageBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ METHOD (showOkCancelBox, "showOkCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ METHOD (showYesNoCancelBox, "showYesNoCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ STATICMETHOD (getLocaleValue, "getLocaleValue", "(Z)Ljava/lang/String;") \ - METHOD (scanFile, "scanFile", "(Ljava/lang/String;)V") + METHOD (scanFile, "scanFile", "(Ljava/lang/String;)V") \ + METHOD (getTypeFaceFromAsset, "getTypeFaceFromAsset", "(Ljava/lang/String;)Landroid/graphics/Typeface;") \ + METHOD (getTypeFaceFromByteArray,"getTypeFaceFromByteArray","([B)Landroid/graphics/Typeface;") DECLARE_JNI_CLASS (JuceAppActivity, JUCE_ANDROID_ACTIVITY_CLASSPATH); #undef JNI_CLASS_MEMBERS diff --git a/source/modules/juce_core/native/juce_android_Network.cpp b/source/modules/juce_core/native/juce_android_Network.cpp index 29a8cb591..46d13fe10 100644 --- a/source/modules/juce_core/native/juce_android_Network.cpp +++ b/source/modules/juce_core/native/juce_android_Network.cpp @@ -70,7 +70,7 @@ class WebInputStream : public InputStream public: WebInputStream (String address, bool isPost, const MemoryBlock& postData, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, - const String& headers, int timeOutMs, StringPairArray* responseHeaders) + const String& headers, int timeOutMs, StringPairArray* responseHeaders, const int numRedirectsToFollow) : statusCode (0) { if (! address.contains ("://")) @@ -103,7 +103,8 @@ public: javaString (headers).get(), (jint) timeOutMs, statusCodeArray, - responseHeaderBuffer.get())); + responseHeaderBuffer.get(), + (jint) numRedirectsToFollow)); jint* const statusCodeElements = env->GetIntArrayElements (statusCodeArray, 0); statusCode = statusCodeElements[0]; diff --git a/source/modules/juce_core/native/juce_android_SystemStats.cpp b/source/modules/juce_core/native/juce_android_SystemStats.cpp index 8223b892c..47ac03aa4 100644 --- a/source/modules/juce_core/native/juce_android_SystemStats.cpp +++ b/source/modules/juce_core/native/juce_android_SystemStats.cpp @@ -278,7 +278,7 @@ String SystemStats::getDisplayLanguage() { return getUserLanguage() + "-" + getU //============================================================================== void CPUInformation::initialise() noexcept { - numCpus = jmax (1, sysconf (_SC_NPROCESSORS_ONLN)); + numCpus = jmax ((int) 1, (int) sysconf (_SC_NPROCESSORS_ONLN)); } //============================================================================== diff --git a/source/modules/juce_core/native/juce_linux_Network.cpp b/source/modules/juce_core/native/juce_linux_Network.cpp index 6871aa546..d07766859 100644 --- a/source/modules/juce_core/native/juce_linux_Network.cpp +++ b/source/modules/juce_core/native/juce_linux_Network.cpp @@ -74,12 +74,13 @@ class WebInputStream : public InputStream public: WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, - const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) + const String& headers_, int timeOutMs_, StringPairArray* responseHeaders, + const int maxRedirects) : statusCode (0), socketHandle (-1), levelsOfRedirection (0), address (address_), headers (headers_), postData (postData_), position (0), - finished (false), isPost (isPost_), timeOutMs (timeOutMs_) + finished (false), isPost (isPost_), timeOutMs (timeOutMs_), numRedirectsToFollow (maxRedirects) { - statusCode = createConnection (progressCallback, progressCallbackContext); + statusCode = createConnection (progressCallback, progressCallbackContext, numRedirectsToFollow); if (responseHeaders != nullptr && ! isError()) { @@ -147,7 +148,7 @@ public: { closeSocket(); position = 0; - statusCode = createConnection (0, 0); + statusCode = createConnection (0, 0, numRedirectsToFollow); } skipNextBytes (wantedPos - position); @@ -168,24 +169,27 @@ private: bool finished; const bool isPost; const int timeOutMs; + const int numRedirectsToFollow; - void closeSocket() + void closeSocket (bool resetLevelsOfRedirection = true) { if (socketHandle >= 0) close (socketHandle); socketHandle = -1; - levelsOfRedirection = 0; + if (resetLevelsOfRedirection) + levelsOfRedirection = 0; } - int createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) + int createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, + const int numRedirectsToFollow) { - closeSocket(); + closeSocket (false); uint32 timeOutTime = Time::getMillisecondCounter(); if (timeOutMs == 0) - timeOutTime += 60000; + timeOutTime += 30000; else if (timeOutMs < 0) timeOutTime = 0xffffffff; else @@ -273,28 +277,28 @@ private: const int status = responseHeader.fromFirstOccurrenceOf (" ", false, false) .substring (0, 3).getIntValue(); - //int contentLength = findHeaderItem (lines, "Content-Length:").getIntValue(); - //bool isChunked = findHeaderItem (lines, "Transfer-Encoding:").equalsIgnoreCase ("chunked"); - String location (findHeaderItem (headerLines, "Location:")); - if (status >= 300 && status < 400 + if (++levelsOfRedirection <= numRedirectsToFollow + && status >= 300 && status < 400 && location.isNotEmpty() && location != address) { - if (! location.startsWithIgnoreCase ("http://")) - location = "http://" + location; - - if (++levelsOfRedirection <= 3) + if (! (location.startsWithIgnoreCase ("http://") + || location.startsWithIgnoreCase ("https://") + || location.startsWithIgnoreCase ("ftp://"))) { - address = location; - return createConnection (progressCallback, progressCallbackContext); + // The following is a bit dodgy. Ideally, we should do a proper transform of the relative URI to a target URI + if (location.startsWithChar ('/')) + location = URL (address).withNewSubPath (location).toString (true); + else + location = address + "/" + location; } + + address = location; + return createConnection (progressCallback, progressCallbackContext, numRedirectsToFollow); } - else - { - levelsOfRedirection = 0; - return status; - } + + return status; } closeSocket(); @@ -363,10 +367,14 @@ private: writeValueIfNotPresent (header, userHeaders, "Connection:", "close"); if (isPost) + { writeValueIfNotPresent (header, userHeaders, "Content-Length:", String ((int) postData.getSize())); - - header << "\r\n" << userHeaders - << "\r\n" << postData; + header << userHeaders << "\r\n" << postData; + } + else + { + header << "\r\n" << userHeaders << "\r\n"; + } return header.getMemoryBlock(); } diff --git a/source/modules/juce_core/native/juce_mac_Network.mm b/source/modules/juce_core/native/juce_mac_Network.mm index 130759e6d..a0ddef572 100644 --- a/source/modules/juce_core/native/juce_mac_Network.mm +++ b/source/modules/juce_core/native/juce_mac_Network.mm @@ -115,7 +115,7 @@ bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& targetEmailA class URLConnectionState : public Thread { public: - URLConnectionState (NSURLRequest* req) + URLConnectionState (NSURLRequest* req, const int maxRedirects) : Thread ("http connection"), contentLength (-1), delegate (nil), @@ -126,7 +126,9 @@ public: statusCode (0), initialised (false), hasFailed (false), - hasFinished (false) + hasFinished (false), + numRedirectsToFollow (maxRedirects), + numRedirects (0) { static DelegateClass cls; delegate = [cls.createInstance() init]; @@ -215,6 +217,19 @@ public: } } + NSURLRequest* willSendRequest (NSURLRequest* newRequest, NSURLResponse* redirectResponse) + { + if (redirectResponse != nullptr) + { + if (numRedirects >= numRedirectsToFollow) + return nil; // Cancel redirect and allow connection to continue + + ++numRedirects; + } + + return newRequest; + } + void didFailWithError (NSError* error) { DBG (nsStringToJuce ([error description])); (void) error; @@ -263,6 +278,8 @@ public: NSDictionary* headers; int statusCode; bool initialised, hasFailed, hasFinished; + const int numRedirectsToFollow; + int numRedirects; private: //============================================================================== @@ -278,7 +295,7 @@ private: addMethod (@selector (connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:), connectionDidSendBodyData, "v@:@iii"); addMethod (@selector (connectionDidFinishLoading:), connectionDidFinishLoading, "v@:@"); - addMethod (@selector (connection:willSendRequest:redirectResponse:), willSendRequest, "@@:@@"); + addMethod (@selector (connection:willSendRequest:redirectResponse:), willSendRequest, "@@:@@@"); registerClass(); } @@ -302,9 +319,9 @@ private: getState (self)->didReceiveData (newData); } - static NSURLRequest* willSendRequest (id, SEL, NSURLConnection*, NSURLRequest* request, NSURLResponse*) + static NSURLRequest* willSendRequest (id self, SEL, NSURLConnection*, NSURLRequest* request, NSURLResponse* response) { - return request; + return getState (self)->willSendRequest (request, response); } static void connectionDidSendBodyData (id self, SEL, NSURLConnection*, NSInteger, NSInteger totalBytesWritten, NSInteger totalBytesExpected) @@ -328,23 +345,27 @@ class WebInputStream : public InputStream public: WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, - const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) + const String& headers_, int timeOutMs_, StringPairArray* responseHeaders, + const int numRedirectsToFollow_) : statusCode (0), address (address_), headers (headers_), postData (postData_), position (0), - finished (false), isPost (isPost_), timeOutMs (timeOutMs_) + finished (false), isPost (isPost_), timeOutMs (timeOutMs_), numRedirectsToFollow (numRedirectsToFollow_) { JUCE_AUTORELEASEPOOL { createConnection (progressCallback, progressCallbackContext); - if (responseHeaders != nullptr && connection != nullptr && connection->headers != nil) + if (connection != nullptr && connection->headers != nil) { statusCode = connection->statusCode; - NSEnumerator* enumerator = [connection->headers keyEnumerator]; + if (responseHeaders != nullptr) + { + NSEnumerator* enumerator = [connection->headers keyEnumerator]; - while (NSString* key = [enumerator nextObject]) - responseHeaders->set (nsStringToJuce (key), - nsStringToJuce ((NSString*) [connection->headers objectForKey: key])); + while (NSString* key = [enumerator nextObject]) + responseHeaders->set (nsStringToJuce (key), + nsStringToJuce ((NSString*) [connection->headers objectForKey: key])); + } } } } @@ -403,6 +424,7 @@ private: bool finished; const bool isPost; const int timeOutMs; + const int numRedirectsToFollow; void createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) { @@ -434,7 +456,7 @@ private: [req setHTTPBody: [NSData dataWithBytes: postData.getData() length: postData.getSize()]]; - connection = new URLConnectionState (req); + connection = new URLConnectionState (req, numRedirectsToFollow); if (! connection->start (progressCallback, progressCallbackContext)) connection = nullptr; diff --git a/source/modules/juce_core/native/juce_mac_Strings.mm b/source/modules/juce_core/native/juce_mac_Strings.mm index 862eb539e..7a38b3d80 100644 --- a/source/modules/juce_core/native/juce_mac_Strings.mm +++ b/source/modules/juce_core/native/juce_mac_Strings.mm @@ -32,17 +32,21 @@ String String::fromCFString (CFStringRef cfString) return String(); CFRange range = { 0, CFStringGetLength (cfString) }; - HeapBlock u ((size_t) range.length + 1); - CFStringGetCharacters (cfString, range, u); - u[range.length] = 0; + CFIndex bytesNeeded = 0; + CFStringGetBytes (cfString, range, kCFStringEncodingUTF8, 0, false, nullptr, 0, &bytesNeeded); - return String (CharPointer_UTF16 ((const CharPointer_UTF16::CharType*) u.getData())); + HeapBlock utf8 ((size_t) bytesNeeded + 1); + CFStringGetBytes (cfString, range, kCFStringEncodingUTF8, 0, false, utf8, bytesNeeded + 1, nullptr); + + return String (CharPointer_UTF8 ((const CharPointer_UTF8::CharType*) utf8.getData()), + CharPointer_UTF8 ((const CharPointer_UTF8::CharType*) utf8.getData() + bytesNeeded)); } CFStringRef String::toCFString() const { - CharPointer_UTF16 utf16 (toUTF16()); - return CFStringCreateWithCharacters (kCFAllocatorDefault, (const UniChar*) utf16.getAddress(), (CFIndex) utf16.length()); + const char* const utf8 = toRawUTF8(); + return CFStringCreateWithBytes (kCFAllocatorDefault, (const UInt8*) utf8, + (CFIndex) strlen (utf8), kCFStringEncodingUTF8, false); } String String::convertToPrecomposedUnicode() const @@ -72,7 +76,7 @@ String String::convertToPrecomposedUnicode() const { const size_t bytesNeeded = CharPointer_UTF16::getBytesRequiredFor (getCharPointer()); - HeapBlock tempOut; + HeapBlock tempOut; tempOut.calloc (bytesNeeded + 4); ByteCount bytesRead = 0; diff --git a/source/modules/juce_core/native/juce_osx_ObjCHelpers.h b/source/modules/juce_core/native/juce_osx_ObjCHelpers.h index 10878d042..d338afd20 100644 --- a/source/modules/juce_core/native/juce_osx_ObjCHelpers.h +++ b/source/modules/juce_core/native/juce_osx_ObjCHelpers.h @@ -66,6 +66,13 @@ namespace static_cast (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; } + + typedef double (*MsgSendFPRetFn) (id, SEL op, ...); + static inline MsgSendFPRetFn getMsgSendFPRetFn() noexcept { return (MsgSendFPRetFn) (void*) objc_msgSend_fpret; } } //============================================================================== @@ -143,7 +150,7 @@ struct ObjCClass static id sendSuperclassMessage (id self, SEL selector) { objc_super s = { self, [SuperclassType class] }; - return objc_msgSendSuper (&s, selector); + return getMsgSendSuperFn() (&s, selector); } template diff --git a/source/modules/juce_core/native/juce_posix_SharedCode.h b/source/modules/juce_core/native/juce_posix_SharedCode.h index 3574420cb..089569744 100644 --- a/source/modules/juce_core/native/juce_posix_SharedCode.h +++ b/source/modules/juce_core/native/juce_posix_SharedCode.h @@ -304,23 +304,33 @@ bool File::hasWriteAccess() const return false; } -bool File::setFileReadOnlyInternal (const bool shouldBeReadOnly) const +static bool setFileModeFlags (const String& fullPath, mode_t flags, bool shouldSet) noexcept { juce_statStruct info; if (! juce_stat (fullPath, info)) return false; - info.st_mode &= 0777; // Just permissions + info.st_mode &= 0777; - if (shouldBeReadOnly) - info.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); + if (shouldSet) + info.st_mode |= flags; else - // Give everybody write permission? - info.st_mode |= S_IWUSR | S_IWGRP | S_IWOTH; + info.st_mode &= ~flags; return chmod (fullPath.toUTF8(), info.st_mode) == 0; } +bool File::setFileReadOnlyInternal (bool shouldBeReadOnly) const +{ + // Hmm.. should we give global write permission or just the current user? + return setFileModeFlags (fullPath, S_IWUSR | S_IWGRP | S_IWOTH, ! shouldBeReadOnly); +} + +bool File::setFileExecutableInternal (bool shouldBeExecutable) const +{ + return setFileModeFlags (fullPath, S_IXUSR | S_IXGRP | S_IXOTH, shouldBeExecutable); +} + void File::getFileTimesInternal (int64& modificationTime, int64& accessTime, int64& creationTime) const { modificationTime = 0; @@ -328,11 +338,12 @@ void File::getFileTimesInternal (int64& modificationTime, int64& accessTime, int creationTime = 0; juce_statStruct info; + if (juce_stat (fullPath, info)) { - modificationTime = (int64) info.st_mtime * 1000; - accessTime = (int64) info.st_atime * 1000; - creationTime = (int64) info.st_ctime * 1000; + modificationTime = (int64) info.st_mtime * 1000; + accessTime = (int64) info.st_atime * 1000; + creationTime = (int64) info.st_ctime * 1000; } } diff --git a/source/modules/juce_core/native/juce_win32_Files.cpp b/source/modules/juce_core/native/juce_win32_Files.cpp index a77aa5aac..b4bbd9b83 100644 --- a/source/modules/juce_core/native/juce_win32_Files.cpp +++ b/source/modules/juce_core/native/juce_win32_Files.cpp @@ -137,17 +137,22 @@ bool File::existsAsFile() const bool File::isDirectory() const { const DWORD attr = WindowsFileHelpers::getAtts (fullPath); - return ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) && (attr != INVALID_FILE_ATTRIBUTES); + return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0 && attr != INVALID_FILE_ATTRIBUTES; } bool File::hasWriteAccess() const { - if (exists()) - return (WindowsFileHelpers::getAtts (fullPath) & FILE_ATTRIBUTE_READONLY) == 0; + if (fullPath.isEmpty()) + return true; - // on windows, it seems that even read-only directories can still be written into, - // so checking the parent directory's permissions would return the wrong result.. - return true; + const DWORD attr = WindowsFileHelpers::getAtts (fullPath); + + // NB: According to MS, the FILE_ATTRIBUTE_READONLY attribute doesn't work for + // folders, and can be incorrectly set for some special folders, so we'll just say + // that folders are always writable. + return attr == INVALID_FILE_ATTRIBUTES + || (attr & FILE_ATTRIBUTE_DIRECTORY) != 0 + || (attr & FILE_ATTRIBUTE_READONLY) == 0; } bool File::setFileReadOnlyInternal (const bool shouldBeReadOnly) const @@ -163,6 +168,12 @@ bool File::setFileReadOnlyInternal (const bool shouldBeReadOnly) const || SetFileAttributes (fullPath.toWideCharPointer(), newAtts) != FALSE; } +bool File::setFileExecutableInternal (bool /*shouldBeExecutable*/) const +{ + // XXX is this possible? + return false; +} + bool File::isHidden() const { return (WindowsFileHelpers::getAtts (fullPath) & FILE_ATTRIBUTE_HIDDEN) != 0; diff --git a/source/modules/juce_core/native/juce_win32_Network.cpp b/source/modules/juce_core/native/juce_win32_Network.cpp index f0b6061b4..7e555cc52 100644 --- a/source/modules/juce_core/native/juce_win32_Network.cpp +++ b/source/modules/juce_core/native/juce_win32_Network.cpp @@ -40,12 +40,12 @@ class WebInputStream : public InputStream public: WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, - const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) + const String& headers_, int timeOutMs_, StringPairArray* responseHeaders, int numRedirectsToFollow) : statusCode (0), connection (0), request (0), address (address_), headers (headers_), postData (postData_), position (0), finished (false), isPost (isPost_), timeOutMs (timeOutMs_) { - for (int maxRedirects = 10; --maxRedirects >= 0;) + while (numRedirectsToFollow-- >= 0) { createConnection (progressCallback, progressCallbackContext); @@ -88,9 +88,22 @@ public: { statusCode = (int) status; - if (status == 301 || status == 302 || status == 303 || status == 307) + if (numRedirectsToFollow >= 0 + && (statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 307)) { - const String newLocation (headers["Location"]); + String newLocation (headers["Location"]); + + // Check whether location is a relative URI - this is an incomplete test for relative path, + // but we'll use it for now (valid protocols for this implementation are http, https & ftp) + if (! (newLocation.startsWithIgnoreCase ("http://") + || newLocation.startsWithIgnoreCase ("https://") + || newLocation.startsWithIgnoreCase ("ftp://"))) + { + if (newLocation.startsWithChar ('/')) + newLocation = URL (address).withNewSubPath (newLocation).toString (true); + else + newLocation = address + "/" + newLocation; + } if (newLocation.isNotEmpty() && newLocation != address) { diff --git a/source/modules/juce_core/network/juce_Socket.cpp b/source/modules/juce_core/network/juce_Socket.cpp index 50da39cbc..291b580a7 100644 --- a/source/modules/juce_core/network/juce_Socket.cpp +++ b/source/modules/juce_core/network/juce_Socket.cpp @@ -389,7 +389,10 @@ void StreamingSocket::close() } if (handle != -1) + { + ::shutdown (handle, SHUT_RDWR); ::close (handle); + } #endif hostName.clear(); diff --git a/source/modules/juce_core/network/juce_URL.cpp b/source/modules/juce_core/network/juce_URL.cpp index 76d2b1e79..f6f8620f9 100644 --- a/source/modules/juce_core/network/juce_URL.cpp +++ b/source/modules/juce_core/network/juce_URL.cpp @@ -334,7 +334,8 @@ InputStream* URL::createInputStream (const bool usePostCommand, String headers, const int timeOutMs, StringPairArray* const responseHeaders, - int* statusCode) const + int* statusCode, + const int numRedirectsToFollow) const { MemoryBlock headersAndPostData; @@ -350,7 +351,8 @@ InputStream* URL::createInputStream (const bool usePostCommand, ScopedPointer wi (new WebInputStream (toString (! usePostCommand), usePostCommand, headersAndPostData, progressCallback, progressCallbackContext, - headers, timeOutMs, responseHeaders)); + headers, timeOutMs, responseHeaders, + numRedirectsToFollow)); if (statusCode != nullptr) *statusCode = wi->statusCode; @@ -397,6 +399,17 @@ URL URL::withParameter (const String& parameterName, return u; } +URL URL::withParameters (const StringPairArray& parametersToAdd) const +{ + URL u (*this); + + for (int i = 0; i < parametersToAdd.size(); ++i) + u.addParameter (parametersToAdd.getAllKeys()[i], + parametersToAdd.getAllValues()[i]); + + return u; +} + URL URL::withPOSTData (const String& newPostData) const { URL u (*this); @@ -482,8 +495,8 @@ String URL::addEscapeChars (const String& s, const bool isParameter) || legalChars.indexOf ((juce_wchar) c) >= 0)) { utf8.set (i, '%'); - utf8.insert (++i, "0123456789abcdef" [((uint8) c) >> 4]); - utf8.insert (++i, "0123456789abcdef" [c & 15]); + utf8.insert (++i, "0123456789ABCDEF" [((uint8) c) >> 4]); + utf8.insert (++i, "0123456789ABCDEF" [c & 15]); } } diff --git a/source/modules/juce_core/network/juce_URL.h b/source/modules/juce_core/network/juce_URL.h index 06d8fb732..d3a87c134 100644 --- a/source/modules/juce_core/network/juce_URL.h +++ b/source/modules/juce_core/network/juce_URL.h @@ -140,6 +140,12 @@ public: URL withParameter (const String& parameterName, const String& parameterValue) const; + /** Returns a copy of this URL, with a set of GET or POST parameters added. + This is a convenience method, equivalent to calling withParameter for each value. + @see withParameter + */ + URL withParameters (const StringPairArray& parametersToAdd) const; + /** Returns a copy of this URL, with a file-upload type parameter added to it. When performing a POST where one of your parameters is a binary file, this @@ -260,6 +266,8 @@ public: in the response will be stored in this array @param statusCode if this is non-null, it will get set to the http status code, if one is known, or 0 if a code isn't available + @param numRedirectsToFollow specifies the number of redirects that will be followed before + returning a response (ignored for Android which follows up to 5 redirects) @returns an input stream that the caller must delete, or a null pointer if there was an error trying to open it. */ @@ -269,7 +277,8 @@ public: String extraHeaders = String(), int connectionTimeOutMs = 0, StringPairArray* responseHeaders = nullptr, - int* statusCode = nullptr) const; + int* statusCode = nullptr, + int numRedirectsToFollow = 5) const; //============================================================================== diff --git a/source/modules/juce_core/streams/juce_MemoryOutputStream.cpp b/source/modules/juce_core/streams/juce_MemoryOutputStream.cpp index adbaed2f2..28a558570 100644 --- a/source/modules/juce_core/streams/juce_MemoryOutputStream.cpp +++ b/source/modules/juce_core/streams/juce_MemoryOutputStream.cpp @@ -89,14 +89,14 @@ char* MemoryOutputStream::prepareToWrite (size_t numBytes) if (storageNeeded >= blockToUse->getSize()) blockToUse->ensureSize ((storageNeeded + jmin (storageNeeded / 2, (size_t) (1024 * 1024)) + 32) & ~31u); - data = static_cast (blockToUse->getData()); + data = static_cast (blockToUse->getData()); } else { if (storageNeeded > availableSize) return nullptr; - data = static_cast (externalData); + data = static_cast (externalData); } char* const writePointer = data + position; @@ -157,7 +157,7 @@ const void* MemoryOutputStream::getData() const noexcept return externalData; if (blockToUse->getSize() > size) - static_cast (blockToUse->getData()) [size] = 0; + static_cast (blockToUse->getData()) [size] = 0; return blockToUse->getData(); } @@ -194,7 +194,7 @@ int64 MemoryOutputStream::writeFromInputStream (InputStream& source, int64 maxNu String MemoryOutputStream::toUTF8() const { - const char* const d = static_cast (getData()); + const char* const d = static_cast (getData()); return String (CharPointer_UTF8 (d), CharPointer_UTF8 (d + getDataSize())); } diff --git a/source/modules/juce_core/system/juce_CompilerSupport.h b/source/modules/juce_core/system/juce_CompilerSupport.h new file mode 100644 index 000000000..dbed46cf5 --- /dev/null +++ b/source/modules/juce_core/system/juce_CompilerSupport.h @@ -0,0 +1,160 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + +#ifndef JUCE_COMPILERSUPPORT_H_INCLUDED +#define JUCE_COMPILERSUPPORT_H_INCLUDED + +/* This file has some checks to see whether the compiler supports various C++11/14 features, + When these aren't available, the code defines a few workarounds, so that we can still use + some of the newer language features like nullptr and noexcept, even on old compilers. +*/ + +//============================================================================== +// GCC +#if (__cplusplus >= 201103L || defined (__GXX_EXPERIMENTAL_CXX0X__)) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 + #define JUCE_COMPILER_SUPPORTS_NOEXCEPT 1 + #define JUCE_COMPILER_SUPPORTS_NULLPTR 1 + #define JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS 1 + #define JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS 1 + #define JUCE_COMPILER_SUPPORTS_VARIADIC_TEMPLATES 1 + + #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && ! defined (JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL) + #define JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL 1 + #endif + + #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && ! defined (JUCE_DELETED_FUNCTION) + #define JUCE_DELETED_FUNCTION = delete + #endif + + #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && ! defined (JUCE_COMPILER_SUPPORTS_LAMBDAS) + #define JUCE_COMPILER_SUPPORTS_LAMBDAS 1 + #endif +#endif + +//============================================================================== +// Clang +#if JUCE_CLANG && defined (__has_feature) + #if __has_feature (cxx_nullptr) + #define JUCE_COMPILER_SUPPORTS_NULLPTR 1 + #endif + + #if __has_feature (cxx_noexcept) + #define JUCE_COMPILER_SUPPORTS_NOEXCEPT 1 + #endif + + #if __has_feature (cxx_rvalue_references) + #define JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS 1 + #endif + + #if __has_feature (cxx_deleted_functions) + #define JUCE_DELETED_FUNCTION = delete + #endif + + #if __has_feature (cxx_lambdas) && (defined (_LIBCPP_VERSION) || ! (JUCE_MAC || JUCE_IOS)) + #define JUCE_COMPILER_SUPPORTS_LAMBDAS 1 + #endif + + #if __has_feature (cxx_generalized_initializers) && (defined (_LIBCPP_VERSION) || ! (JUCE_MAC || JUCE_IOS)) + #define JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS 1 + #endif + + #if __has_feature (cxx_variadic_templates) + #define JUCE_COMPILER_SUPPORTS_VARIADIC_TEMPLATES 1 + #endif + + #ifndef JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL + #define JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL 1 + #endif + + #ifndef JUCE_COMPILER_SUPPORTS_ARC + #define JUCE_COMPILER_SUPPORTS_ARC 1 + #endif + +#endif + +//============================================================================== +// MSVC +#ifdef _MSC_VER + #if _MSC_VER >= 1600 + #define JUCE_COMPILER_SUPPORTS_NULLPTR 1 + #define JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS 1 + #endif + + #if _MSC_VER >= 1700 + #define JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL 1 + #define JUCE_COMPILER_SUPPORTS_LAMBDAS 1 + #endif + + #if _MSC_VER >= 1800 + #define JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS 1 + #define JUCE_COMPILER_SUPPORTS_VARIADIC_TEMPLATES 1 + #endif + + #if _MSC_VER >= 1900 + #define JUCE_COMPILER_SUPPORTS_NOEXCEPT 1 + #define JUCE_DELETED_FUNCTION = delete + #endif +#endif + +//============================================================================== +// Declare some fake versions of nullptr and noexcept, for older compilers: + +#ifndef JUCE_DELETED_FUNCTION + /** This macro can be placed after a method declaration to allow the use of + the C++11 feature "= delete" on all compilers. + On newer compilers that support it, it does the C++11 "= delete", but on + older ones it's just an empty definition. + */ + #define JUCE_DELETED_FUNCTION +#endif + +#if ! DOXYGEN + #if ! JUCE_COMPILER_SUPPORTS_NOEXCEPT + #ifdef noexcept + #undef noexcept + #endif + #define noexcept throw() + #if defined (_MSC_VER) && _MSC_VER > 1600 + #define _ALLOW_KEYWORD_MACROS 1 // (to stop VC2012 complaining) + #endif + #endif + + #if ! JUCE_COMPILER_SUPPORTS_NULLPTR + #ifdef nullptr + #undef nullptr + #endif + #define nullptr (0) + #endif + + #if ! JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL + #undef override + #define override + #endif +#endif + +#endif // JUCE_COMPILERSUPPORT_H_INCLUDED diff --git a/source/modules/juce_core/system/juce_PlatformDefs.h b/source/modules/juce_core/system/juce_PlatformDefs.h index ee6532d5c..3252fdbba 100644 --- a/source/modules/juce_core/system/juce_PlatformDefs.h +++ b/source/modules/juce_core/system/juce_PlatformDefs.h @@ -336,96 +336,4 @@ namespace juce #define JUCE_PACKED #endif -//============================================================================== -// Here, we'll check for C++11 compiler support, and if it's not available, define -// a few workarounds, so that we can still use some of the newer language features. -#if (__cplusplus >= 201103L || defined (__GXX_EXPERIMENTAL_CXX0X__)) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 - #define JUCE_COMPILER_SUPPORTS_NOEXCEPT 1 - #define JUCE_COMPILER_SUPPORTS_NULLPTR 1 - #define JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS 1 - - #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && ! defined (JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL) - #define JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL 1 - #endif - - #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && ! defined (JUCE_DELETED_FUNCTION) - #define JUCE_DELETED_FUNCTION = delete - #endif - - #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && ! defined (JUCE_COMPILER_SUPPORTS_LAMBDAS) - #define JUCE_COMPILER_SUPPORTS_LAMBDAS 1 - #endif -#endif - -#if JUCE_CLANG && defined (__has_feature) - #if __has_feature (cxx_nullptr) - #define JUCE_COMPILER_SUPPORTS_NULLPTR 1 - #endif - - #if __has_feature (cxx_noexcept) - #define JUCE_COMPILER_SUPPORTS_NOEXCEPT 1 - #endif - - #if __has_feature (cxx_rvalue_references) - #define JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS 1 - #endif - - #if __has_feature (cxx_deleted_functions) - #define JUCE_DELETED_FUNCTION = delete - #endif - - #if __has_feature (cxx_lambdas) \ - && ((JUCE_MAC && defined (MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_8) \ - || (JUCE_IOS && defined (__IPHONE_7_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0) \ - || ! (JUCE_MAC || JUCE_IOS)) - #define JUCE_COMPILER_SUPPORTS_LAMBDAS 1 - #endif - - #ifndef JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL - #define JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL 1 - #endif - - #ifndef JUCE_COMPILER_SUPPORTS_ARC - #define JUCE_COMPILER_SUPPORTS_ARC 1 - #endif -#endif - -#if defined (_MSC_VER) && _MSC_VER >= 1600 - #define JUCE_COMPILER_SUPPORTS_NULLPTR 1 - #define JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS 1 -#endif - -#if defined (_MSC_VER) && _MSC_VER >= 1700 - #define JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL 1 - #define JUCE_COMPILER_SUPPORTS_LAMBDAS 1 -#endif - -#ifndef JUCE_DELETED_FUNCTION - #define JUCE_DELETED_FUNCTION -#endif - -//============================================================================== -// Declare some fake versions of nullptr and noexcept, for older compilers: -#if ! (DOXYGEN || JUCE_COMPILER_SUPPORTS_NOEXCEPT) - #ifdef noexcept - #undef noexcept - #endif - #define noexcept throw() - #if defined (_MSC_VER) && _MSC_VER > 1600 - #define _ALLOW_KEYWORD_MACROS 1 // (to stop VC2012 complaining) - #endif -#endif - -#if ! (DOXYGEN || JUCE_COMPILER_SUPPORTS_NULLPTR) - #ifdef nullptr - #undef nullptr - #endif - #define nullptr (0) -#endif - -#if ! (DOXYGEN || JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL) - #undef override - #define override -#endif - #endif // JUCE_PLATFORMDEFS_H_INCLUDED diff --git a/source/modules/juce_core/system/juce_StandardHeader.h b/source/modules/juce_core/system/juce_StandardHeader.h index fcfd4c22e..b70c98163 100644 --- a/source/modules/juce_core/system/juce_StandardHeader.h +++ b/source/modules/juce_core/system/juce_StandardHeader.h @@ -35,8 +35,8 @@ See also SystemStats::getJUCEVersion() for a string version. */ #define JUCE_MAJOR_VERSION 3 -#define JUCE_MINOR_VERSION 0 -#define JUCE_BUILDNUMBER 8 +#define JUCE_MINOR_VERSION 1 +#define JUCE_BUILDNUMBER 1 /** Current Juce version number. @@ -50,7 +50,10 @@ //============================================================================== +#include // included before platform defs to provide a definition of _LIBCPP_VERSION + #include "juce_PlatformDefs.h" +#include "juce_CompilerSupport.h" //============================================================================== // Now we'll include some common OS headers.. @@ -70,7 +73,6 @@ #include #include #include -#include #include #include @@ -103,7 +105,7 @@ #endif #if JUCE_ANDROID - #include + #include #include #endif diff --git a/source/modules/juce_core/system/juce_SystemStats.h b/source/modules/juce_core/system/juce_SystemStats.h index 835e6e617..023dcc0a4 100644 --- a/source/modules/juce_core/system/juce_SystemStats.h +++ b/source/modules/juce_core/system/juce_SystemStats.h @@ -47,28 +47,29 @@ 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, - - 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, + + 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). */ }; /** Returns the type of operating system we're running on. diff --git a/source/modules/juce_core/text/juce_CharPointer_ASCII.h b/source/modules/juce_core/text/juce_CharPointer_ASCII.h index a966543b5..a8e8de841 100644 --- a/source/modules/juce_core/text/juce_CharPointer_ASCII.h +++ b/source/modules/juce_core/text/juce_CharPointer_ASCII.h @@ -276,8 +276,10 @@ public: int compareIgnoreCase (const CharPointer_ASCII other) const { - #if JUCE_WINDOWS + #if JUCE_MSVC return stricmp (data, other.data); + #elif JUCE_MINGW + return CharacterFunctions::compareIgnoreCase (*this, other); #else return strcasecmp (data, other.data); #endif @@ -344,7 +346,7 @@ public: /** Parses this string as a 64-bit integer. */ int64 getIntValue64() const noexcept { - #if JUCE_LINUX || JUCE_ANDROID + #if JUCE_LINUX || JUCE_ANDROID || JUCE_MINGW return atoll (data); #elif JUCE_WINDOWS return _atoi64 (data); diff --git a/source/modules/juce_core/text/juce_CharPointer_UTF16.h b/source/modules/juce_core/text/juce_CharPointer_UTF16.h index d0eba5cea..50d18901b 100644 --- a/source/modules/juce_core/text/juce_CharPointer_UTF16.h +++ b/source/modules/juce_core/text/juce_CharPointer_UTF16.h @@ -349,7 +349,7 @@ public: return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars); } - #if JUCE_WINDOWS && ! DOXYGEN + #if JUCE_MSVC && ! DOXYGEN int compareIgnoreCase (const CharPointer_UTF16 other) const noexcept { return _wcsicmp (data, other.data); @@ -408,7 +408,7 @@ public: /** Parses this string as a 32-bit integer. */ int getIntValue32() const noexcept { - #if JUCE_WINDOWS + #if JUCE_MSVC return _wtoi (data); #else return CharacterFunctions::getIntValue (*this); @@ -418,7 +418,7 @@ public: /** Parses this string as a 64-bit integer. */ int64 getIntValue64() const noexcept { - #if JUCE_WINDOWS + #if JUCE_MSVC return _wtoi64 (data); #else return CharacterFunctions::getIntValue (*this); diff --git a/source/modules/juce_core/text/juce_CharPointer_UTF8.h b/source/modules/juce_core/text/juce_CharPointer_UTF8.h index 78d9a975e..f6e156e14 100644 --- a/source/modules/juce_core/text/juce_CharPointer_UTF8.h +++ b/source/modules/juce_core/text/juce_CharPointer_UTF8.h @@ -421,8 +421,10 @@ public: /** Compares this string with another one. */ int compareIgnoreCase (const CharPointer_UTF8 other) const noexcept { - #if JUCE_WINDOWS + #if JUCE_MSVC return stricmp (data, other.data); + #elif JUCE_MINGW + return CharacterFunctions::compareIgnoreCase (*this, other); #else return strcasecmp (data, other.data); #endif @@ -456,9 +458,9 @@ public: } /** Returns true if the first character of this string is whitespace. */ - bool isWhitespace() const noexcept { return *data == ' ' || (*data <= 13 && *data >= 9); } + bool isWhitespace() const noexcept { const CharType c = *data; return c == ' ' || (c <= 13 && c >= 9); } /** Returns true if the first character of this string is a digit. */ - bool isDigit() const noexcept { return *data >= '0' && *data <= '9'; } + bool isDigit() const noexcept { const CharType c = *data; return c >= '0' && c <= '9'; } /** Returns true if the first character of this string is a letter. */ bool isLetter() const noexcept { return CharacterFunctions::isLetter (operator*()) != 0; } /** Returns true if the first character of this string is a letter or digit. */ @@ -479,7 +481,7 @@ public: /** Parses this string as a 64-bit integer. */ int64 getIntValue64() const noexcept { - #if JUCE_LINUX || JUCE_ANDROID + #if JUCE_LINUX || JUCE_ANDROID || JUCE_MINGW return atoll (data); #elif JUCE_WINDOWS return _atoi64 (data); diff --git a/source/modules/juce_core/text/juce_String.cpp b/source/modules/juce_core/text/juce_String.cpp index 1dce72309..3dbc0c08e 100644 --- a/source/modules/juce_core/text/juce_String.cpp +++ b/source/modules/juce_core/text/juce_String.cpp @@ -566,9 +566,9 @@ struct HashGenerator enum { multiplier = sizeof (Type) > 4 ? 101 : 31 }; }; -int String::hashCode() const noexcept { return HashGenerator ::calculate (text); } -int64 String::hashCode64() const noexcept { return HashGenerator ::calculate (text); } -std::size_t String::hash() const noexcept { return HashGenerator::calculate (text); } +int String::hashCode() const noexcept { return HashGenerator ::calculate (text); } +int64 String::hashCode64() const noexcept { return HashGenerator ::calculate (text); } +size_t String::hash() const noexcept { return HashGenerator ::calculate (text); } //============================================================================== JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const String& s2) noexcept { return s1.compare (s2) == 0; } @@ -720,7 +720,8 @@ int String::compareNatural (StringRef other) const noexcept //============================================================================== void String::append (const String& textToAppend, size_t maxCharsToTake) { - appendCharPointer (textToAppend.text, maxCharsToTake); + appendCharPointer (this == &textToAppend ? String (textToAppend).text + : textToAppend.text, maxCharsToTake); } void String::appendCharPointer (const CharPointerType textToAppend) @@ -765,6 +766,9 @@ String& String::operator+= (const String& other) if (isEmpty()) return operator= (other); + if (this == &other) + return operator+= (String (*this)); + appendCharPointer (other.text); return *this; } diff --git a/source/modules/juce_core/text/juce_String.h b/source/modules/juce_core/text/juce_String.h index 6a511070b..0260acff1 100644 --- a/source/modules/juce_core/text/juce_String.h +++ b/source/modules/juce_core/text/juce_String.h @@ -181,7 +181,7 @@ public: int64 hashCode64() const noexcept; /** Generates a probably-unique hashcode from this string. */ - std::size_t hash() const noexcept; + size_t hash() const noexcept; /** Returns the number of characters in the string. */ int length() const noexcept; diff --git a/source/modules/juce_core/text/juce_StringArray.cpp b/source/modules/juce_core/text/juce_StringArray.cpp index 6c4913862..05311ff24 100644 --- a/source/modules/juce_core/text/juce_StringArray.cpp +++ b/source/modules/juce_core/text/juce_StringArray.cpp @@ -86,6 +86,13 @@ StringArray& StringArray::operator= (StringArray&& other) noexcept } #endif +#if JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS +StringArray::StringArray (const std::initializer_list& stringList) +{ + strings.addArray (stringList); +} +#endif + StringArray::~StringArray() { } diff --git a/source/modules/juce_core/text/juce_StringArray.h b/source/modules/juce_core/text/juce_StringArray.h index 52335e1b4..c3a1a17b6 100644 --- a/source/modules/juce_core/text/juce_StringArray.h +++ b/source/modules/juce_core/text/juce_StringArray.h @@ -86,6 +86,10 @@ public: */ StringArray (const wchar_t* const* strings, int numberOfStrings); + #if JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS + StringArray (const std::initializer_list& strings); + #endif + /** Destructor. */ ~StringArray(); diff --git a/source/modules/juce_core/text/juce_StringPool.h b/source/modules/juce_core/text/juce_StringPool.h index 4de218655..64d034e2b 100644 --- a/source/modules/juce_core/text/juce_StringPool.h +++ b/source/modules/juce_core/text/juce_StringPool.h @@ -88,6 +88,8 @@ private: uint32 lastGarbageCollectionTime; void garbageCollectIfNeeded(); + + JUCE_DECLARE_NON_COPYABLE (StringPool) }; diff --git a/source/modules/juce_core/time/juce_Time.cpp b/source/modules/juce_core/time/juce_Time.cpp index fabcfde2e..7ba8e5f83 100644 --- a/source/modules/juce_core/time/juce_Time.cpp +++ b/source/modules/juce_core/time/juce_Time.cpp @@ -361,7 +361,7 @@ String Time::getTimeZone() const noexcept { String zone[2]; - #if JUCE_WINDOWS + #if JUCE_MSVC _tzset(); #ifdef _INC_TIME_INL @@ -378,7 +378,11 @@ String Time::getTimeZone() const noexcept zone[1] = zonePtr[1]; #endif #else + #if JUCE_MINGW + #warning "Can't find a replacement for tzset on mingw - ideas welcome!" + #else tzset(); + #endif const char** const zonePtr = (const char**) tzname; zone[0] = zonePtr[0]; zone[1] = zonePtr[1]; diff --git a/source/modules/juce_core/xml/juce_XmlDocument.cpp b/source/modules/juce_core/xml/juce_XmlDocument.cpp index 9d27a428e..655f885ef 100644 --- a/source/modules/juce_core/xml/juce_XmlDocument.cpp +++ b/source/modules/juce_core/xml/juce_XmlDocument.cpp @@ -555,7 +555,8 @@ void XmlDocument::readChildElements (XmlElement& parent) else // must be a character block { input = preWhitespaceInput; // roll back to include the leading whitespace - MemoryOutputStream textElementStream; + MemoryOutputStream textElementContent; + bool contentShouldBeUsed = ! ignoreEmptyTextElements; for (;;) { @@ -602,28 +603,20 @@ void XmlDocument::readChildElements (XmlElement& parent) input = entity.getCharPointer(); outOfData = false; - for (;;) - { - XmlElement* const n = readNextElement (true); - - if (n == nullptr) - break; - + while (XmlElement* n = readNextElement (true)) childAppender.append (n); - } input = oldInput; outOfData = oldOutOfData; } else { - textElementStream << entity; + textElementContent << entity; + contentShouldBeUsed = contentShouldBeUsed || entity.containsNonWhitespaceChars(); } } else { - const String::CharPointerType start (input); - for (;;) { const juce_wchar nextChar = *input; @@ -638,19 +631,15 @@ void XmlDocument::readChildElements (XmlElement& parent) return; } + textElementContent.appendUTF8Char (nextChar); + contentShouldBeUsed = contentShouldBeUsed || ! CharacterFunctions::isWhitespace (nextChar); ++input; } - - String content; - content.appendCharPointer (start, input); - textElementStream << content; } } - String textElementContent (textElementStream.toString()); - - if ((! ignoreEmptyTextElements) || textElementContent.containsNonWhitespaceChars()) - childAppender.append (XmlElement::createTextElement (textElementContent)); + if (contentShouldBeUsed) + childAppender.append (XmlElement::createTextElement (textElementContent.toUTF8())); } } } diff --git a/source/modules/juce_core/xml/juce_XmlDocument.h b/source/modules/juce_core/xml/juce_XmlDocument.h index d2a5b49e0..c032b854d 100644 --- a/source/modules/juce_core/xml/juce_XmlDocument.h +++ b/source/modules/juce_core/xml/juce_XmlDocument.h @@ -41,7 +41,7 @@ @code XmlDocument myDocument (File ("myfile.xml")); - XmlElement* mainElement = myDocument.getDocumentElement(); + ScopedPointer mainElement (myDocument.getDocumentElement()); if (mainElement == nullptr) { @@ -57,7 +57,7 @@ Or you can use the static helper methods for quick parsing.. @code - XmlElement* xml = XmlDocument::parse (myXmlFile); + ScopedPointer xml (XmlDocument::parse (myXmlFile)); if (xml != nullptr && xml->hasTagName ("foobar")) { diff --git a/source/modules/juce_data_structures/juce_data_structures.cpp b/source/modules/juce_data_structures/juce_data_structures.cpp index fd1d12aac..e2e8e8189 100644 --- a/source/modules/juce_data_structures/juce_data_structures.cpp +++ b/source/modules/juce_data_structures/juce_data_structures.cpp @@ -42,6 +42,7 @@ namespace juce #include "values/juce_Value.cpp" #include "values/juce_ValueTree.cpp" +#include "values/juce_ValueTreeSynchroniser.cpp" #include "undomanager/juce_UndoManager.cpp" #include "app_properties/juce_ApplicationProperties.cpp" #include "app_properties/juce_PropertiesFile.cpp" diff --git a/source/modules/juce_data_structures/juce_data_structures.h b/source/modules/juce_data_structures/juce_data_structures.h index 629b88ddf..e80d1cea5 100644 --- a/source/modules/juce_data_structures/juce_data_structures.h +++ b/source/modules/juce_data_structures/juce_data_structures.h @@ -35,6 +35,7 @@ namespace juce #include "undomanager/juce_UndoManager.h" #include "values/juce_Value.h" #include "values/juce_ValueTree.h" +#include "values/juce_ValueTreeSynchroniser.h" #include "app_properties/juce_PropertiesFile.h" #include "app_properties/juce_ApplicationProperties.h" diff --git a/source/modules/juce_data_structures/values/juce_Value.cpp b/source/modules/juce_data_structures/values/juce_Value.cpp index 55b37fe17..b0bb40507 100644 --- a/source/modules/juce_data_structures/values/juce_Value.cpp +++ b/source/modules/juce_data_structures/values/juce_Value.cpp @@ -111,12 +111,6 @@ Value::Value (const Value& other) : value (other.value) { } -Value& Value::operator= (const Value& other) -{ - value = other.value; - return *this; -} - #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Value::Value (Value&& other) noexcept { diff --git a/source/modules/juce_data_structures/values/juce_Value.h b/source/modules/juce_data_structures/values/juce_Value.h index f8fc631f3..c25d35d05 100644 --- a/source/modules/juce_data_structures/values/juce_Value.h +++ b/source/modules/juce_data_structures/values/juce_Value.h @@ -221,7 +221,11 @@ private: // This is disallowed to avoid confusion about whether it should // do a by-value or by-reference copy. - Value& operator= (const Value&); + Value& operator= (const Value&) JUCE_DELETED_FUNCTION; + + // This declaration prevents accidental construction from an integer of 0, + // which is possible in some compilers via an implicit cast to a pointer. + explicit Value (void*) JUCE_DELETED_FUNCTION; }; /** Writes a Value to an OutputStream as a UTF8 string. */ diff --git a/source/modules/juce_data_structures/values/juce_ValueTree.cpp b/source/modules/juce_data_structures/values/juce_ValueTree.cpp index ee1340051..667d30d09 100644 --- a/source/modules/juce_data_structures/values/juce_ValueTree.cpp +++ b/source/modules/juce_data_structures/values/juce_ValueTree.cpp @@ -62,23 +62,20 @@ public: { const int numListeners = valueTreesWithListeners.size(); - if (numListeners > 0) + if (numListeners == 1) { - if (numListeners == 1) - { - valueTreesWithListeners.getUnchecked(0)->listeners.call (method, tree); - } - else - { - const SortedSet listenersCopy (valueTreesWithListeners); + valueTreesWithListeners.getUnchecked(0)->listeners.call (method, tree); + } + else if (numListeners > 0) + { + const SortedSet listenersCopy (valueTreesWithListeners); - for (int i = 0; i < numListeners; ++i) - { - ValueTree* const v = listenersCopy.getUnchecked(i); + for (int i = 0; i < numListeners; ++i) + { + ValueTree* const v = listenersCopy.getUnchecked(i); - if (i == 0 || valueTreesWithListeners.contains (v)) - v->listeners.call (method, tree); - } + if (i == 0 || valueTreesWithListeners.contains (v)) + v->listeners.call (method, tree); } } } @@ -88,23 +85,43 @@ public: { const int numListeners = valueTreesWithListeners.size(); - if (numListeners > 0) + if (numListeners == 1) { - if (numListeners == 1) + valueTreesWithListeners.getUnchecked(0)->listeners.call (method, tree, param2); + } + else if (numListeners > 0) + { + const SortedSet listenersCopy (valueTreesWithListeners); + + for (int i = 0; i < numListeners; ++i) { - valueTreesWithListeners.getUnchecked(0)->listeners.call (method, tree, param2); + ValueTree* const v = listenersCopy.getUnchecked(i); + + if (i == 0 || valueTreesWithListeners.contains (v)) + v->listeners.call (method, tree, param2); } - else - { - const SortedSet listenersCopy (valueTreesWithListeners); + } + } - for (int i = 0; i < numListeners; ++i) - { - ValueTree* const v = listenersCopy.getUnchecked(i); + template + void callListeners (Method method, ValueTree& tree, ParamType1& param2, ParamType2& param3) const + { + const int numListeners = valueTreesWithListeners.size(); - if (i == 0 || valueTreesWithListeners.contains (v)) - v->listeners.call (method, tree, param2); - } + if (numListeners == 1) + { + valueTreesWithListeners.getUnchecked(0)->listeners.call (method, tree, param2, param3); + } + else if (numListeners > 0) + { + const SortedSet listenersCopy (valueTreesWithListeners); + + for (int i = 0; i < numListeners; ++i) + { + ValueTree* const v = listenersCopy.getUnchecked(i); + + if (i == 0 || valueTreesWithListeners.contains (v)) + v->listeners.call (method, tree, param2, param3); } } } @@ -125,20 +142,20 @@ public: t->callListeners (&ValueTree::Listener::valueTreeChildAdded, tree, child); } - void sendChildRemovedMessage (ValueTree child) + void sendChildRemovedMessage (ValueTree child, int index) { ValueTree tree (this); for (ValueTree::SharedObject* t = this; t != nullptr; t = t->parent) - t->callListeners (&ValueTree::Listener::valueTreeChildRemoved, tree, child); + t->callListeners (&ValueTree::Listener::valueTreeChildRemoved, tree, child, index); } - void sendChildOrderChangedMessage() + void sendChildOrderChangedMessage (int oldIndex, int newIndex) { ValueTree tree (this); for (ValueTree::SharedObject* t = this; t != nullptr; t = t->parent) - t->callListeners (&ValueTree::Listener::valueTreeChildOrderChanged, tree); + t->callListeners (&ValueTree::Listener::valueTreeChildOrderChanged, tree, oldIndex, newIndex); } void sendParentChangeMessage() @@ -323,7 +340,7 @@ public: { children.remove (childIndex); child->parent = nullptr; - sendChildRemovedMessage (ValueTree (child)); + sendChildRemovedMessage (ValueTree (child), childIndex); child->sendParentChangeMessage(); } else @@ -350,7 +367,7 @@ public: if (undoManager == nullptr) { children.move (currentIndex, newIndex); - sendChildOrderChangedMessage(); + sendChildOrderChangedMessage (currentIndex, newIndex); } else { @@ -366,28 +383,15 @@ public: { jassert (newOrder.size() == children.size()); - if (undoManager == nullptr) + for (int i = 0; i < children.size(); ++i) { - children.clear(); - children.ensureStorageAllocated (newOrder.size()); - - for (int i = 0; i < newOrder.size(); ++i) - children.add (newOrder.getUnchecked(i)->object); + SharedObject* const child = newOrder.getUnchecked(i)->object; - sendChildOrderChangedMessage(); - } - else - { - for (int i = 0; i < children.size(); ++i) + if (children.getObjectPointerUnchecked (i) != child) { - SharedObject* const child = newOrder.getUnchecked(i)->object; - - if (children.getObjectPointerUnchecked (i) != child) - { - const int oldIndex = children.indexOf (child); - jassert (oldIndex >= 0); - moveChild (oldIndex, i, undoManager); - } + const int oldIndex = children.indexOf (child); + jassert (oldIndex >= 0); + moveChild (oldIndex, i, undoManager); } } } @@ -492,7 +496,7 @@ public: { if (! (isAddingNewProperty || isDeletingProperty)) { - if (SetPropertyAction* const next = dynamic_cast (nextAction)) + if (SetPropertyAction* const next = dynamic_cast (nextAction)) if (next->target == target && next->name == name && ! (next->isAddingNewProperty || next->isDeletingProperty)) return new SetPropertyAction (target, name, next->newValue, oldValue, false, false); @@ -592,7 +596,7 @@ public: UndoableAction* createCoalescedAction (UndoableAction* nextAction) { - if (MoveChildAction* next = dynamic_cast (nextAction)) + if (MoveChildAction* next = dynamic_cast (nextAction)) if (next->parent == parent && next->startIndex == endIndex) return new MoveChildAction (parent, startIndex, next->endIndex); @@ -711,7 +715,7 @@ Identifier ValueTree::getType() const ValueTree ValueTree::getParent() const { return ValueTree (object != nullptr ? object->parent - : static_cast (nullptr)); + : static_cast (nullptr)); } ValueTree ValueTree::getSibling (const int delta) const @@ -825,8 +829,8 @@ private: } void valueTreeChildAdded (ValueTree&, ValueTree&) override {} - void valueTreeChildRemoved (ValueTree&, ValueTree&) override {} - void valueTreeChildOrderChanged (ValueTree&) override {} + void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override {} + void valueTreeChildOrderChanged (ValueTree&, int, int) override {} void valueTreeParentChanged (ValueTree&) override {} JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreePropertyValueSource) @@ -846,7 +850,7 @@ int ValueTree::getNumChildren() const ValueTree ValueTree::getChild (int index) const { return ValueTree (object != nullptr ? object->children.getObjectPointer (index) - : static_cast (nullptr)); + : static_cast (nullptr)); } ValueTree ValueTree::getChildWithName (const Identifier type) const @@ -1108,8 +1112,8 @@ public: ValueTree v2 = ValueTree::readFromStream (mi); expect (v1.isEquivalentTo (v2)); - ScopedPointer xml1 (v1.createXml()); - ScopedPointer xml2 (v2.createCopy().createXml()); + ScopedPointer xml1 (v1.createXml()); + ScopedPointer xml2 (v2.createCopy().createXml()); expect (xml1->isEquivalentTo (xml2, false)); ValueTree v4 = v2.createCopy(); diff --git a/source/modules/juce_data_structures/values/juce_ValueTree.h b/source/modules/juce_data_structures/values/juce_ValueTree.h index 42dd77811..9a9ccd16d 100644 --- a/source/modules/juce_data_structures/values/juce_ValueTree.h +++ b/source/modules/juce_data_structures/values/juce_ValueTree.h @@ -403,7 +403,8 @@ public: just check the parentTree parameter to make sure it's the one that you're interested in. */ virtual void valueTreeChildRemoved (ValueTree& parentTree, - ValueTree& childWhichHasBeenRemoved) = 0; + ValueTree& childWhichHasBeenRemoved, + int indexFromWhichChildWasRemoved) = 0; /** This method is called when a tree's children have been re-shuffled. @@ -412,7 +413,8 @@ public: If your tree has sub-trees but you only want to know about changes to the top level tree, just check the parameter to make sure it's the tree that you're interested in. */ - virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved) = 0; + virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved, + int oldIndex, int newIndex) = 0; /** This method is called when a tree has been added or removed from a parent node. @@ -482,7 +484,7 @@ public: { OwnedArray sortedList; createListOfChildren (sortedList); - ComparatorAdapter adapter (comparator); + ComparatorAdapter adapter (comparator); sortedList.sort (adapter, retainOrderOfEquivalentItems); reorderChildren (sortedList, undoManager); } diff --git a/source/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.cpp b/source/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.cpp new file mode 100644 index 000000000..027620786 --- /dev/null +++ b/source/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.cpp @@ -0,0 +1,217 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +namespace ValueTreeSynchroniserHelpers +{ + enum ChangeType + { + propertyChanged = 1, + fullSync = 2, + childAdded = 3, + childRemoved = 4, + childMoved = 5 + }; + + static void getValueTreePath (ValueTree v, const ValueTree& topLevelTree, Array& path) + { + while (v != topLevelTree) + { + ValueTree parent (v.getParent()); + + if (! parent.isValid()) + break; + + path.add (parent.indexOf (v)); + v = parent; + } + } + + static void writeHeader (MemoryOutputStream& stream, ChangeType type) + { + stream.writeByte ((char) type); + } + + static void writeHeader (ValueTreeSynchroniser& target, MemoryOutputStream& stream, + ChangeType type, ValueTree v) + { + writeHeader (stream, type); + + Array path; + getValueTreePath (v, target.getRoot(), path); + + stream.writeCompressedInt (path.size()); + + for (int i = path.size(); --i >= 0;) + stream.writeCompressedInt (path.getUnchecked(i)); + } + + static ValueTree readSubTreeLocation (MemoryInputStream& input, ValueTree v) + { + const int numLevels = input.readCompressedInt(); + + if (! isPositiveAndBelow (numLevels, 65536)) // sanity-check + return ValueTree(); + + for (int i = numLevels; --i >= 0;) + { + const int index = input.readCompressedInt(); + + if (! isPositiveAndBelow (index, v.getNumChildren())) + return ValueTree(); + + v = v.getChild (index); + } + + return v; + } +} + +ValueTreeSynchroniser::ValueTreeSynchroniser (const ValueTree& tree) : valueTree (tree) +{ + valueTree.addListener (this); +} + +ValueTreeSynchroniser::~ValueTreeSynchroniser() +{ + valueTree.removeListener (this); +} + +void ValueTreeSynchroniser::sendFullSyncCallback() +{ + MemoryOutputStream m; + writeHeader (m, ValueTreeSynchroniserHelpers::fullSync); + valueTree.writeToStream (m); + stateChanged (m.getData(), m.getDataSize()); +} + +void ValueTreeSynchroniser::valueTreePropertyChanged (ValueTree& vt, const Identifier& property) +{ + MemoryOutputStream m; + ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::propertyChanged, vt); + m.writeString (property.toString()); + vt.getProperty (property).writeToStream (m); + stateChanged (m.getData(), m.getDataSize()); +} + +void ValueTreeSynchroniser::valueTreeChildAdded (ValueTree& parentTree, ValueTree& childTree) +{ + const int index = parentTree.indexOf (childTree); + jassert (index >= 0); + + MemoryOutputStream m; + ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childAdded, parentTree); + m.writeCompressedInt (index); + childTree.writeToStream (m); + stateChanged (m.getData(), m.getDataSize()); +} + +void ValueTreeSynchroniser::valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int oldIndex) +{ + MemoryOutputStream m; + ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childRemoved, parentTree); + m.writeCompressedInt (oldIndex); + stateChanged (m.getData(), m.getDataSize()); +} + +void ValueTreeSynchroniser::valueTreeChildOrderChanged (ValueTree& parent, int oldIndex, int newIndex) +{ + MemoryOutputStream m; + ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childMoved, parent); + m.writeCompressedInt (oldIndex); + m.writeCompressedInt (newIndex); + stateChanged (m.getData(), m.getDataSize()); +} + +void ValueTreeSynchroniser::valueTreeParentChanged (ValueTree&) {} // (No action needed here) + +bool ValueTreeSynchroniser::applyChange (ValueTree& root, const void* data, size_t dataSize, UndoManager* undoManager) +{ + MemoryInputStream input (data, dataSize, false); + + const ValueTreeSynchroniserHelpers::ChangeType type = (ValueTreeSynchroniserHelpers::ChangeType) input.readByte(); + + if (type == ValueTreeSynchroniserHelpers::fullSync) + { + root = ValueTree::readFromStream (input); + return true; + } + + ValueTree v (ValueTreeSynchroniserHelpers::readSubTreeLocation (input, root)); + + if (! v.isValid()) + return false; + + switch (type) + { + case ValueTreeSynchroniserHelpers::propertyChanged: + { + Identifier property (input.readString()); + v.setProperty (property, var::readFromStream (input), undoManager); + return true; + } + + case ValueTreeSynchroniserHelpers::childAdded: + { + const int index = input.readCompressedInt(); + v.addChild (ValueTree::readFromStream (input), index, undoManager); + return true; + } + + case ValueTreeSynchroniserHelpers::childRemoved: + { + const int index = input.readCompressedInt(); + + if (isPositiveAndBelow (index, v.getNumChildren())) + { + v.removeChild (index, undoManager); + return true; + } + + jassertfalse; // Either received some corrupt data, or the trees have drifted out of sync + break; + } + + case ValueTreeSynchroniserHelpers::childMoved: + { + const int oldIndex = input.readCompressedInt(); + const int newIndex = input.readCompressedInt(); + + if (isPositiveAndBelow (oldIndex, v.getNumChildren()) + && isPositiveAndBelow (newIndex, v.getNumChildren())) + { + v.moveChild (oldIndex, newIndex, undoManager); + return true; + } + + jassertfalse; // Either received some corrupt data, or the trees have drifted out of sync + break; + } + + default: + jassertfalse; // Seem to have received some corrupt data? + break; + } + + return false; +} diff --git a/source/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.h b/source/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.h new file mode 100644 index 000000000..8cb4fbef6 --- /dev/null +++ b/source/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.h @@ -0,0 +1,98 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_VALUETREESYNCHRONISER_H_INCLUDED +#define JUCE_VALUETREESYNCHRONISER_H_INCLUDED + + +//============================================================================== +/** + This class can be used to watch for all changes to the state of a ValueTree, + and to convert them to a transmittable binary encoding. + + The purpose of this class is to allow two or more ValueTrees to be remotely + synchronised by transmitting encoded changes over some kind of transport + mechanism. + + To use it, you'll need to implement a subclass of ValueTreeSynchroniser + and implement the stateChanged() method to transmit the encoded change (maybe + via a network or other means) to a remote destination, where it can be + applied to a target tree. +*/ +class JUCE_API ValueTreeSynchroniser : private ValueTree::Listener +{ +public: + /** Creates a ValueTreeSynchroniser that watches the given tree. + + After creating an instance of this class and somehow attaching it to + a target tree, you probably want to call sendFullSyncCallback() to + get them into a common starting state. + */ + ValueTreeSynchroniser (const ValueTree& tree); + + /** Destructor. */ + virtual ~ValueTreeSynchroniser(); + + /** This callback happens when the ValueTree changes and the given state-change message + needs to be applied to any other trees that need to stay in sync with it. + The data is an opaque blob of binary that you should transmit to wherever your + target tree lives, and use applyChange() to apply this to the target tree. + */ + virtual void stateChanged (const void* encodedChange, size_t encodedChangeSize) = 0; + + /** Forces the sending of a full state message, which may be large, as it + encodes the entire ValueTree. + + This will internally invoke stateChanged() with the encoded version of the state. + */ + void sendFullSyncCallback(); + + /** Applies an encoded change to the given destination tree. + + When you implement a receiver for changes that were sent by the stateChanged() + message, this is the function that you'll need to call to apply them to the + target tree that you want to be synced. + */ + static bool applyChange (ValueTree& target, + const void* encodedChangeData, size_t encodedChangeDataSize, + UndoManager* undoManager); + + /** Returns the root ValueTree that is being observed. */ + const ValueTree& getRoot() noexcept { return valueTree; } + +private: + ValueTree valueTree; + + void valueTreePropertyChanged (ValueTree&, const Identifier&) override; + void valueTreeChildAdded (ValueTree&, ValueTree&) override; + void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override; + void valueTreeChildOrderChanged (ValueTree&, int, int) override; + void valueTreeParentChanged (ValueTree&) override; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreeSynchroniser) +}; + + + +#endif // JUCE_VALUETREESYNCHRONISER_H_INCLUDED diff --git a/source/modules/juce_events/juce_events.h b/source/modules/juce_events/juce_events.h index 02f7c6393..7af9c9c53 100644 --- a/source/modules/juce_events/juce_events.h +++ b/source/modules/juce_events/juce_events.h @@ -39,6 +39,7 @@ namespace juce #include "messages/juce_NotificationType.h" #include "messages/juce_ApplicationBase.h" #include "messages/juce_Initialisation.h" +#include "messages/juce_MountedVolumeListChangeDetector.h" #include "broadcasters/juce_ListenerList.h" #include "broadcasters/juce_ActionBroadcaster.h" #include "broadcasters/juce_ActionListener.h" diff --git a/source/modules/juce_events/messages/juce_MessageManager.cpp b/source/modules/juce_events/messages/juce_MessageManager.cpp index 44c2adc80..8f4f92abe 100644 --- a/source/modules/juce_events/messages/juce_MessageManager.cpp +++ b/source/modules/juce_events/messages/juce_MessageManager.cpp @@ -81,11 +81,6 @@ bool MessageManager::MessageBase::post() //============================================================================== #if JUCE_MODAL_LOOPS_PERMITTED && ! (JUCE_MAC || JUCE_IOS) -void MessageManager::runDispatchLoop() -{ - runDispatchLoopUntil (-1); -} - bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) { jassert (isThisTheMessageThread()); // must only be called by the message thread @@ -107,7 +102,9 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) return ! quitMessageReceived; } +#endif +#if ! (JUCE_MAC || JUCE_IOS || JUCE_ANDROID) class MessageManager::QuitMessage : public MessageManager::MessageBase { public: @@ -122,6 +119,21 @@ public: JUCE_DECLARE_NON_COPYABLE (QuitMessage) }; +void MessageManager::runDispatchLoop() +{ + jassert (isThisTheMessageThread()); // must only be called by the message thread + + while (! quitMessageReceived) + { + JUCE_TRY + { + if (! dispatchNextMessageOnSystemQueue (false)) + Thread::sleep (1); + } + JUCE_CATCH_EXCEPTION + } +} + void MessageManager::stopDispatchLoop() { (new QuitMessage())->post(); diff --git a/source/modules/juce_events/messages/juce_MountedVolumeListChangeDetector.h b/source/modules/juce_events/messages/juce_MountedVolumeListChangeDetector.h new file mode 100644 index 000000000..fe6287543 --- /dev/null +++ b/source/modules/juce_events/messages/juce_MountedVolumeListChangeDetector.h @@ -0,0 +1,59 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_MOUNTEDVOLUMELISTCHANGEDETECTOR_H_INCLUDED +#define JUCE_MOUNTEDVOLUMELISTCHANGEDETECTOR_H_INCLUDED + +#if JUCE_MAC || JUCE_WINDOWS || defined (DOXYGEN) + +//============================================================================== +/** + An instance of this class will provide callbacks when drives are + mounted or unmounted on the system. + + Just inherit from this class and implement the pure virtual method + to get the callbacks, there's no need to do anything else. + + @see File::findFileSystemRoots() +*/ +class JUCE_API MountedVolumeListChangeDetector +{ +public: + MountedVolumeListChangeDetector(); + virtual ~MountedVolumeListChangeDetector(); + + /** This method is called when a volume is mounted or unmounted. */ + virtual void mountedVolumeListChanged() = 0; + +private: + JUCE_PUBLIC_IN_DLL_BUILD (struct Pimpl) + friend struct ContainerDeletePolicy; + ScopedPointer pimpl; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MountedVolumeListChangeDetector) +}; + +#endif + +#endif // JUCE_MOUNTEDVOLUMELISTCHANGEDETECTOR_H_INCLUDED diff --git a/source/modules/juce_events/native/juce_android_Messaging.cpp b/source/modules/juce_events/native/juce_android_Messaging.cpp index 733994ad6..144a32646 100644 --- a/source/modules/juce_events/native/juce_android_Messaging.cpp +++ b/source/modules/juce_events/native/juce_android_Messaging.cpp @@ -42,7 +42,7 @@ bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* cons return true; } -JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, deliverMessage, void, (jobject activity, jlong value)) +JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, deliverMessage, void, (JNIEnv* env, jobject activity, jlong value)) { JUCE_TRY { diff --git a/source/modules/juce_events/native/juce_linux_Messaging.cpp b/source/modules/juce_events/native/juce_linux_Messaging.cpp index 9f17ab1c8..8134b26e5 100644 --- a/source/modules/juce_events/native/juce_linux_Messaging.cpp +++ b/source/modules/juce_events/native/juce_linux_Messaging.cpp @@ -37,8 +37,8 @@ typedef void (*SelectionRequestCallback) (XSelectionRequestEvent&); SelectionRequestCallback handleSelectionRequest = nullptr; //============================================================================== -ScopedXLock::ScopedXLock() { XLockDisplay (display); } -ScopedXLock::~ScopedXLock() { XUnlockDisplay (display); } +ScopedXLock::ScopedXLock() { if (display != nullptr) XLockDisplay (display); } +ScopedXLock::~ScopedXLock() { if (display != nullptr) XUnlockDisplay (display); } //============================================================================== class InternalMessageQueue @@ -101,7 +101,7 @@ public: if (! isEmpty()) return true; - if (display != 0) + if (display != nullptr) { ScopedXLock xlock; if (XPending (display)) @@ -118,7 +118,7 @@ public: FD_ZERO (&readset); FD_SET (fd0, &readset); - if (display != 0) + if (display != nullptr) { ScopedXLock xlock; int fd1 = XConnectionNumber (display); @@ -131,7 +131,7 @@ public: } //============================================================================== - juce_DeclareSingleton_SingleThreaded_Minimal (InternalMessageQueue); + juce_DeclareSingleton_SingleThreaded_Minimal (InternalMessageQueue) private: CriticalSection lock; @@ -154,7 +154,7 @@ private: static bool dispatchNextXEvent() { - if (display == 0) + if (display == nullptr) return false; XEvent evt; @@ -209,7 +209,7 @@ private: } }; -juce_ImplementSingleton_SingleThreaded (InternalMessageQueue); +juce_ImplementSingleton_SingleThreaded (InternalMessageQueue) //============================================================================== @@ -319,7 +319,7 @@ void MessageManager::doPlatformSpecificInitialisation() display = XOpenDisplay (displayName.toUTF8()); - if (display != 0) // This is not fatal! we can run headless. + if (display != nullptr) // This is not fatal! we can run headless. { // Create a context to store user data associated with Windows we create windowHandleXContext = XUniqueContext(); @@ -341,7 +341,7 @@ void MessageManager::doPlatformSpecificShutdown() { InternalMessageQueue::deleteInstance(); - if (display != 0 && ! LinuxErrorHandling::errorOccurred) + if (display != nullptr && ! LinuxErrorHandling::errorOccurred) { XDestroyWindow (display, juce_messageWindowHandle); XCloseDisplay (display); diff --git a/source/modules/juce_events/native/juce_mac_MessageManager.mm b/source/modules/juce_events/native/juce_mac_MessageManager.mm index 3aed8a24b..0090d0bd4 100644 --- a/source/modules/juce_events/native/juce_mac_MessageManager.mm +++ b/source/modules/juce_events/native/juce_mac_MessageManager.mm @@ -53,7 +53,7 @@ public: [[NSDistributedNotificationCenter defaultCenter] addObserver: delegate selector: @selector (broadcastMessageCallback:) - name: getBroacastEventName() + name: getBroadcastEventName() object: nil]; } else @@ -79,14 +79,14 @@ public: [NSApp setDelegate: nil]; [[NSDistributedNotificationCenter defaultCenter] removeObserver: delegate - name: getBroacastEventName() + name: getBroadcastEventName() object: nil]; } [delegate release]; } - static NSString* getBroacastEventName() + static NSString* getBroadcastEventName() { return juceStringToNS ("juce_" + String::toHexString (File::getSpecialLocation (File::currentExecutableFile).hashCode64())); } @@ -96,26 +96,36 @@ public: private: //============================================================================== - struct AppDelegateClass : public ObjCClass + struct AppDelegateClass : public ObjCClass { - AppDelegateClass() : ObjCClass ("JUCEAppDelegate_") + AppDelegateClass() : ObjCClass ("JUCEAppDelegate_") { - addMethod (@selector (applicationShouldTerminate:), applicationShouldTerminate, "I@:@"); - addMethod (@selector (applicationWillTerminate:), applicationWillTerminate, "v@:@"); - addMethod (@selector (application:openFile:), application_openFile, "c@:@@"); - addMethod (@selector (application:openFiles:), application_openFiles, "v@:@@"); - addMethod (@selector (applicationDidBecomeActive:), applicationDidBecomeActive, "v@:@"); - addMethod (@selector (applicationDidResignActive:), applicationDidResignActive, "v@:@"); - addMethod (@selector (applicationWillUnhide:), applicationWillUnhide, "v@:@"); - addMethod (@selector (broadcastMessageCallback:), broadcastMessageCallback, "v@:@"); - addMethod (@selector (mainMenuTrackingBegan:), mainMenuTrackingBegan, "v@:@"); - addMethod (@selector (mainMenuTrackingEnded:), mainMenuTrackingEnded, "v@:@"); - addMethod (@selector (dummyMethod), dummyMethod, "v@:"); + addMethod (@selector (applicationWillFinishLaunching:), applicationWillFinishLaunching, "v@:@@"); + addMethod (@selector (getUrl:withReplyEvent:), getUrl_withReplyEvent, "v@:@@"); + addMethod (@selector (applicationShouldTerminate:), applicationShouldTerminate, "I@:@"); + addMethod (@selector (applicationWillTerminate:), applicationWillTerminate, "v@:@"); + addMethod (@selector (application:openFile:), application_openFile, "c@:@@"); + addMethod (@selector (application:openFiles:), application_openFiles, "v@:@@"); + addMethod (@selector (applicationDidBecomeActive:), applicationDidBecomeActive, "v@:@"); + addMethod (@selector (applicationDidResignActive:), applicationDidResignActive, "v@:@"); + addMethod (@selector (applicationWillUnhide:), applicationWillUnhide, "v@:@"); + addMethod (@selector (broadcastMessageCallback:), broadcastMessageCallback, "v@:@"); + addMethod (@selector (mainMenuTrackingBegan:), mainMenuTrackingBegan, "v@:@"); + addMethod (@selector (mainMenuTrackingEnded:), mainMenuTrackingEnded, "v@:@"); + addMethod (@selector (dummyMethod), dummyMethod, "v@:"); registerClass(); } private: + static void applicationWillFinishLaunching (id self, SEL, NSApplication*, NSNotification*) + { + [[NSAppleEventManager sharedAppleEventManager] setEventHandler: self + andSelector: @selector (getUrl:withReplyEvent:) + forEventClass: kInternetEventClass + andEventID: kAEGetURL]; + } + static NSApplicationTerminateReply applicationShouldTerminate (id /*self*/, SEL, NSApplication*) { if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance()) @@ -184,13 +194,18 @@ private: static void dummyMethod (id /*self*/, SEL) {} // (used as a way of running a dummy thread) - private: static void focusChanged() { if (appFocusChangeCallback != nullptr) (*appFocusChangeCallback)(); } + static void getUrl_withReplyEvent (id /*self*/, SEL, NSAppleEventDescriptor* event, NSAppleEventDescriptor*) + { + if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance()) + app->anotherInstanceStarted (quotedIfContainsSpaces ([[event paramDescriptorForKeyword: keyDirectObject] stringValue])); + } + static String quotedIfContainsSpaces (NSString* file) { String s (nsStringToJuce (file)); @@ -341,7 +356,7 @@ void MessageManager::broadcastMessage (const String& message) NSDictionary* info = [NSDictionary dictionaryWithObject: juceStringToNS (message) forKey: nsStringLiteral ("message")]; - [[NSDistributedNotificationCenter defaultCenter] postNotificationName: AppDelegate::getBroacastEventName() + [[NSDistributedNotificationCenter defaultCenter] postNotificationName: AppDelegate::getBroadcastEventName() object: nil userInfo: info]; } @@ -365,3 +380,54 @@ void repostCurrentNSEvent() (new EventReposter())->post(); } + + +//============================================================================== +#if JUCE_MAC +struct MountedVolumeListChangeDetector::Pimpl +{ + Pimpl (MountedVolumeListChangeDetector& d) : owner (d) + { + static ObserverClass cls; + delegate = [cls.createInstance() init]; + ObserverClass::setOwner (delegate, this); + + NSNotificationCenter* nc = [[NSWorkspace sharedWorkspace] notificationCenter]; + + [nc addObserver: delegate selector: @selector (changed:) name: NSWorkspaceDidMountNotification object: nil]; + [nc addObserver: delegate selector: @selector (changed:) name: NSWorkspaceDidUnmountNotification object: nil]; + } + + ~Pimpl() + { + [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver: delegate]; + [delegate release]; + } + +private: + MountedVolumeListChangeDetector& owner; + id delegate; + + struct ObserverClass : public ObjCClass + { + ObserverClass() : ObjCClass ("JUCEDriveObserver_") + { + addIvar ("owner"); + addMethod (@selector (changed:), changed, "v@:@"); + addProtocol (@protocol (NSTextInput)); + registerClass(); + } + + static Pimpl* getOwner (id self) { return getIvar (self, "owner"); } + static void setOwner (id self, Pimpl* owner) { object_setInstanceVariable (self, "owner", owner); } + + static void changed (id self, SEL, NSNotification*) + { + getOwner (self)->owner.mountedVolumeListChanged(); + } + }; +}; + +MountedVolumeListChangeDetector::MountedVolumeListChangeDetector() { pimpl = new Pimpl (*this); } +MountedVolumeListChangeDetector::~MountedVolumeListChangeDetector() {} +#endif diff --git a/source/modules/juce_events/native/juce_win32_HiddenMessageWindow.h b/source/modules/juce_events/native/juce_win32_HiddenMessageWindow.h index c4747ec13..e0df59ad0 100644 --- a/source/modules/juce_events/native/juce_win32_HiddenMessageWindow.h +++ b/source/modules/juce_events/native/juce_win32_HiddenMessageWindow.h @@ -100,7 +100,6 @@ public: virtual ~DeviceChangeDetector() {} -protected: virtual void systemDeviceChanged() = 0; void triggerAsyncDeviceChangeCallback() diff --git a/source/modules/juce_events/native/juce_win32_Messaging.cpp b/source/modules/juce_events/native/juce_win32_Messaging.cpp index 0bc0f7810..24bc41a32 100644 --- a/source/modules/juce_events/native/juce_win32_Messaging.cpp +++ b/source/modules/juce_events/native/juce_win32_Messaging.cpp @@ -38,7 +38,7 @@ namespace WindowsMessageHelpers void dispatchMessageFromLParam (LPARAM lParam) { - MessageManager::MessageBase* const message = reinterpret_cast (lParam); + MessageManager::MessageBase* const message = reinterpret_cast (lParam); JUCE_TRY { @@ -61,15 +61,17 @@ namespace WindowsMessageHelpers dispatchMessageFromLParam (lParam); return 0; } - else if (message == broadcastId) + + if (message == broadcastId) { const ScopedPointer messageString ((String*) lParam); MessageManager::getInstance()->deliverBroadcastMessage (*messageString); return 0; } - else if (message == WM_COPYDATA) + + if (message == WM_COPYDATA) { - const COPYDATASTRUCT* const data = reinterpret_cast (lParam); + const COPYDATASTRUCT* const data = reinterpret_cast (lParam); if (data->dwData == broadcastId) { @@ -88,7 +90,7 @@ namespace WindowsMessageHelpers BOOL CALLBACK broadcastEnumWindowProc (HWND hwnd, LPARAM lParam) { if (hwnd != juce_messageWindowHandle) - reinterpret_cast *> (lParam)->add (hwnd); + reinterpret_cast*> (lParam)->add (hwnd); return TRUE; } @@ -157,9 +159,8 @@ void MessageManager::broadcastMessage (const String& value) { HWND hwnd = windows.getUnchecked(i); - TCHAR windowName [64]; // no need to read longer strings than this - GetWindowText (hwnd, windowName, 64); - windowName [63] = 0; + TCHAR windowName[64] = { 0 }; // no need to read longer strings than this + GetWindowText (hwnd, windowName, 63); if (String (windowName) == WindowsMessageHelpers::messageWindowName) { @@ -188,3 +189,30 @@ void MessageManager::doPlatformSpecificShutdown() OleUninitialize(); } + +//============================================================================== +struct MountedVolumeListChangeDetector::Pimpl : private DeviceChangeDetector +{ + Pimpl (MountedVolumeListChangeDetector& d) : DeviceChangeDetector (L"MountedVolumeList"), owner (d) + { + File::findFileSystemRoots (lastVolumeList); + } + + void systemDeviceChanged() override + { + Array newList; + File::findFileSystemRoots (newList); + + if (lastVolumeList != newList) + { + lastVolumeList = newList; + owner.mountedVolumeListChanged(); + } + } + + MountedVolumeListChangeDetector& owner; + Array lastVolumeList; +}; + +MountedVolumeListChangeDetector::MountedVolumeListChangeDetector() { pimpl = new Pimpl (*this); } +MountedVolumeListChangeDetector::~MountedVolumeListChangeDetector() {} diff --git a/source/modules/juce_events/timers/juce_Timer.cpp b/source/modules/juce_events/timers/juce_Timer.cpp index 6b7343a55..dfde957a7 100644 --- a/source/modules/juce_events/timers/juce_Timer.cpp +++ b/source/modules/juce_events/timers/juce_Timer.cpp @@ -186,7 +186,7 @@ public: private: Timer* volatile firstTimer; - Atomic callbackNeeded; + Atomic callbackNeeded; struct CallTimersMessage : public MessageManager::MessageBase { @@ -332,6 +332,14 @@ void Timer::startTimer (const int interval) noexcept } } +void Timer::startTimerHz (int timerFrequencyHz) noexcept +{ + if (timerFrequencyHz > 0) + startTimer (1000 / timerFrequencyHz); + else + stopTimer(); +} + void Timer::stopTimer() noexcept { const TimerThread::LockType::ScopedLockType sl (TimerThread::lock); diff --git a/source/modules/juce_events/timers/juce_Timer.h b/source/modules/juce_events/timers/juce_Timer.h index a2f823af2..57b0ce821 100644 --- a/source/modules/juce_events/timers/juce_Timer.h +++ b/source/modules/juce_events/timers/juce_Timer.h @@ -91,6 +91,11 @@ public: */ void startTimer (int intervalInMilliseconds) noexcept; + /** Starts the timer with an interval specified in Hertz. + This is effectively the same as calling startTimer (1000 / timerFrequencyHz). + */ + void startTimerHz (int timerFrequencyHz) noexcept; + /** Stops the timer. No more callbacks will be made after this method returns. @@ -102,14 +107,10 @@ public: void stopTimer() noexcept; //============================================================================== - /** Checks if the timer has been started. - - @returns true if the timer is running. - */ + /** Returns true if the timer is currently running. */ bool isTimerRunning() const noexcept { return periodMs > 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; } diff --git a/source/modules/juce_graphics/colour/juce_ColourGradient.cpp b/source/modules/juce_graphics/colour/juce_ColourGradient.cpp index 75d171295..8e10816c1 100644 --- a/source/modules/juce_graphics/colour/juce_ColourGradient.cpp +++ b/source/modules/juce_graphics/colour/juce_ColourGradient.cpp @@ -32,12 +32,12 @@ ColourGradient::ColourGradient() noexcept #endif } -ColourGradient::ColourGradient (Colour colour1, const float x1_, const float y1_, - Colour colour2, const float x2_, const float y2_, - const bool isRadial_) - : point1 (x1_, y1_), - point2 (x2_, y2_), - isRadial (isRadial_) +ColourGradient::ColourGradient (Colour colour1, const float x1, const float y1, + Colour colour2, const float x2, const float y2, + const bool radial) + : point1 (x1, y1), + point2 (x2, y2), + isRadial (radial) { colours.add (ColourPoint (0.0, colour1)); colours.add (ColourPoint (1.0, colour2)); diff --git a/source/modules/juce_graphics/effects/juce_DropShadowEffect.cpp b/source/modules/juce_graphics/effects/juce_DropShadowEffect.cpp index 448fab876..603d034fb 100644 --- a/source/modules/juce_graphics/effects/juce_DropShadowEffect.cpp +++ b/source/modules/juce_graphics/effects/juce_DropShadowEffect.cpp @@ -116,11 +116,11 @@ void DropShadow::drawForPath (Graphics& g, const Path& path) const } } -static void drawShadowSection (Graphics& g, ColourGradient& cg, const Rectangle& area, +static void drawShadowSection (Graphics& g, ColourGradient& cg, Rectangle area, bool isCorner, float centreX, float centreY, float edgeX, float edgeY) { - cg.point1 = area.getRelativePoint (centreX, centreY).toFloat(); - cg.point2 = area.getRelativePoint (edgeX, edgeY).toFloat(); + cg.point1 = area.getRelativePoint (centreX, centreY); + cg.point2 = area.getRelativePoint (edgeX, edgeY); cg.isRadial = isCorner; g.setGradientFill (cg); @@ -134,14 +134,14 @@ void DropShadow::drawForRectangle (Graphics& g, const Rectangle& targetArea for (float i = 0.05f; i < 1.0f; i += 0.1f) cg.addColour (1.0 - i, colour.withMultipliedAlpha (i * i)); - const int radiusInset = (radius + 1) / 2; - const int expandedRadius = radius + radiusInset; + const float radiusInset = (radius + 1) / 2.0f; + const float expandedRadius = radius + radiusInset; - const Rectangle area (targetArea.reduced (radiusInset) + offset); + const Rectangle area (targetArea.toFloat().reduced (radiusInset) + offset.toFloat()); - Rectangle r (area.expanded (expandedRadius)); - Rectangle top (r.removeFromTop (expandedRadius)); - Rectangle bottom (r.removeFromBottom (expandedRadius)); + Rectangle r (area.expanded (expandedRadius)); + Rectangle top (r.removeFromTop (expandedRadius)); + Rectangle bottom (r.removeFromBottom (expandedRadius)); drawShadowSection (g, cg, top.removeFromLeft (expandedRadius), true, 1.0f, 1.0f, 0, 1.0f); drawShadowSection (g, cg, top.removeFromRight (expandedRadius), true, 0, 1.0f, 1.0f, 1.0f); diff --git a/source/modules/juce_graphics/fonts/juce_Font.cpp b/source/modules/juce_graphics/fonts/juce_Font.cpp index 1b4fc04ab..f0668d1d7 100644 --- a/source/modules/juce_graphics/fonts/juce_Font.cpp +++ b/source/modules/juce_graphics/fonts/juce_Font.cpp @@ -51,7 +51,7 @@ public: clearSingletonInstance(); } - juce_DeclareSingleton (TypefaceCache, false); + juce_DeclareSingleton (TypefaceCache, false) void setSize (const int numToCache) { diff --git a/source/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp b/source/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp index 8ad3b7b2f..7b0de6ef5 100644 --- a/source/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp +++ b/source/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp @@ -75,20 +75,20 @@ PositionedGlyph& PositionedGlyph::operator= (const PositionedGlyph& other) return *this; } -static inline void drawGlyphWithFont (const Graphics& g, int glyph, const Font& font, const AffineTransform& t) +static inline void drawGlyphWithFont (Graphics& g, int glyph, const Font& font, const AffineTransform& t) { LowLevelGraphicsContext& context = g.getInternalContext(); context.setFont (font); context.drawGlyph (glyph, t); } -void PositionedGlyph::draw (const Graphics& g) const +void PositionedGlyph::draw (Graphics& g) const { if (! isWhitespace()) drawGlyphWithFont (g, glyph, font, AffineTransform::translation (x, y)); } -void PositionedGlyph::draw (const Graphics& g, const AffineTransform& transform) const +void PositionedGlyph::draw (Graphics& g, const AffineTransform& transform) const { if (! isWhitespace()) drawGlyphWithFont (g, glyph, font, AffineTransform::translation (x, y).followedBy (transform)); @@ -223,16 +223,14 @@ void GlyphArrangement::addCurtailedLineOfText (const Font& font, break; } - else - { - const float thisX = xOffsets.getUnchecked (i); - const bool isWhitespace = t.isWhitespace(); - glyphs.add (PositionedGlyph (font, t.getAndAdvance(), - newGlyphs.getUnchecked(i), - xOffset + thisX, yOffset, - nextX - thisX, isWhitespace)); - } + const float thisX = xOffsets.getUnchecked (i); + const bool isWhitespace = t.isWhitespace(); + + glyphs.add (PositionedGlyph (font, t.getAndAdvance(), + newGlyphs.getUnchecked(i), + xOffset + thisX, yOffset, + nextX - thisX, isWhitespace)); } } } @@ -316,7 +314,8 @@ void GlyphArrangement::addJustifiedText (const Font& font, break; } - else if (pg.isWhitespace()) + + if (pg.isWhitespace()) { lastWordBreakIndex = i + 1; } @@ -758,19 +757,15 @@ void GlyphArrangement::drawGlyphUnderline (const Graphics& g, const PositionedGl void GlyphArrangement::draw (const Graphics& g) const { - for (int i = 0; i < glyphs.size(); ++i) - { - const PositionedGlyph& pg = glyphs.getReference(i); - - if (pg.font.isUnderlined()) - drawGlyphUnderline (g, pg, i, AffineTransform::identity); - - pg.draw (g); - } + draw (g, AffineTransform()); } void GlyphArrangement::draw (const Graphics& g, const AffineTransform& transform) const { + LowLevelGraphicsContext& context = g.getInternalContext(); + Font lastFont (context.getFont()); + bool needToRestore = false; + for (int i = 0; i < glyphs.size(); ++i) { const PositionedGlyph& pg = glyphs.getReference(i); @@ -778,8 +773,27 @@ void GlyphArrangement::draw (const Graphics& g, const AffineTransform& transform if (pg.font.isUnderlined()) drawGlyphUnderline (g, pg, i, transform); - pg.draw (g, transform); + if (! pg.isWhitespace()) + { + if (lastFont != pg.font) + { + lastFont = pg.font; + + if (! needToRestore) + { + needToRestore = true; + context.saveState(); + } + + context.setFont (lastFont); + } + + context.drawGlyph (pg.glyph, AffineTransform::translation (pg.x, pg.y).followedBy (transform)); + } } + + if (needToRestore) + context.restoreState(); } void GlyphArrangement::createPath (Path& path) const diff --git a/source/modules/juce_graphics/fonts/juce_GlyphArrangement.h b/source/modules/juce_graphics/fonts/juce_GlyphArrangement.h index ad27a57ed..c771bffe6 100644 --- a/source/modules/juce_graphics/fonts/juce_GlyphArrangement.h +++ b/source/modules/juce_graphics/fonts/juce_GlyphArrangement.h @@ -77,14 +77,17 @@ public: void moveBy (float deltaX, float deltaY); //============================================================================== - /** Draws the glyph into a graphics context. */ - void draw (const Graphics& g) const; + /** Draws the glyph into a graphics context. + (Note that this may change the context's currently selected font). + */ + void draw (Graphics& g) const; - /** Draws the glyph into a graphics context, with an extra transform applied to it. */ - void draw (const Graphics& g, const AffineTransform& transform) const; + /** Draws the glyph into a graphics context, with an extra transform applied to it. + (Note that this may change the context's currently selected font). + */ + void draw (Graphics& g, const AffineTransform& transform) const; /** Returns the path for this glyph. - @param path the glyph's outline will be appended to this path */ void createPath (Path& path) const; diff --git a/source/modules/juce_graphics/fonts/juce_TextLayout.cpp b/source/modules/juce_graphics/fonts/juce_TextLayout.cpp index 526cac1a9..1989bc7b7 100644 --- a/source/modules/juce_graphics/fonts/juce_TextLayout.cpp +++ b/source/modules/juce_graphics/fonts/juce_TextLayout.cpp @@ -22,8 +22,8 @@ ============================================================================== */ -TextLayout::Glyph::Glyph (const int glyphCode_, Point anchor_, float width_) noexcept - : glyphCode (glyphCode_), anchor (anchor_), width (width_) +TextLayout::Glyph::Glyph (const int glyph, Point anch, float w) noexcept + : glyphCode (glyph), anchor (anch), width (w) { } @@ -70,11 +70,10 @@ TextLayout::Line::Line() noexcept { } -TextLayout::Line::Line (Range stringRange_, Point lineOrigin_, - const float ascent_, const float descent_, const float leading_, - const int numRunsToPreallocate) - : stringRange (stringRange_), lineOrigin (lineOrigin_), - ascent (ascent_), descent (descent_), leading (leading_) +TextLayout::Line::Line (Range range, Point o, float asc, float desc, + float lead, int numRunsToPreallocate) + : stringRange (range), lineOrigin (o), + ascent (asc), descent (desc), leading (lead) { runs.ensureStorageAllocated (numRunsToPreallocate); } @@ -127,14 +126,28 @@ Range TextLayout::Line::getLineBoundsX() const noexcept return range + lineOrigin.x; } +Range TextLayout::Line::getLineBoundsY() const noexcept +{ + return Range (lineOrigin.y - ascent, + lineOrigin.y + descent); +} + +Rectangle TextLayout::Line::getLineBounds() const noexcept +{ + const Range x (getLineBoundsX()), + y (getLineBoundsY()); + + return Rectangle (x.getStart(), y.getStart(), x.getLength(), y.getLength()); +} + //============================================================================== TextLayout::TextLayout() - : width (0), justification (Justification::topLeft) + : width (0), height (0), justification (Justification::topLeft) { } TextLayout::TextLayout (const TextLayout& other) - : width (other.width), + : width (other.width), height (other.height), justification (other.justification) { lines.addCopiesOf (other.lines); @@ -142,16 +155,17 @@ TextLayout::TextLayout (const TextLayout& other) #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS TextLayout::TextLayout (TextLayout&& other) noexcept - : lines (static_cast &&> (other.lines)), - width (other.width), + : lines (static_cast&&> (other.lines)), + width (other.width), height (other.height), justification (other.justification) { } TextLayout& TextLayout::operator= (TextLayout&& other) noexcept { - lines = static_cast &&> (other.lines); + lines = static_cast&&> (other.lines); width = other.width; + height = other.height; justification = other.justification; return *this; } @@ -160,6 +174,7 @@ TextLayout& TextLayout::operator= (TextLayout&& other) noexcept TextLayout& TextLayout::operator= (const TextLayout& other) { width = other.width; + height = other.height; justification = other.justification; lines.clear(); lines.addCopiesOf (other.lines); @@ -170,17 +185,9 @@ TextLayout::~TextLayout() { } -float TextLayout::getHeight() const noexcept -{ - if (const Line* const lastLine = lines.getLast()) - return lastLine->lineOrigin.y + lastLine->descent; - - return 0.0f; -} - TextLayout::Line& TextLayout::getLine (const int index) const { - return *lines[index]; + return *lines.getUnchecked (index); } void TextLayout::ensureStorageAllocated (int numLinesNeeded) @@ -199,7 +206,7 @@ void TextLayout::draw (Graphics& g, const Rectangle& area) const LowLevelGraphicsContext& context = g.getInternalContext(); - for (int i = 0; i < getNumLines(); ++i) + for (int i = 0; i < lines.size(); ++i) { const Line& line = getLine (i); const Point lineOrigin (origin + line.lineOrigin); @@ -221,15 +228,60 @@ void TextLayout::draw (Graphics& g, const Rectangle& area) const } void TextLayout::createLayout (const AttributedString& text, float maxWidth) +{ + createLayout (text, maxWidth, 1.0e7f); +} + +void TextLayout::createLayout (const AttributedString& text, float maxWidth, float maxHeight) { lines.clear(); width = maxWidth; + height = maxHeight; justification = text.getJustification(); if (! createNativeLayout (text)) createStandardLayout (text); - recalculateWidth (text); + recalculateSize (text); +} + +void TextLayout::createLayoutWithBalancedLineLengths (const AttributedString& text, float maxWidth) +{ + createLayoutWithBalancedLineLengths (text, maxWidth, 1.0e7f); +} + +void TextLayout::createLayoutWithBalancedLineLengths (const AttributedString& text, float maxWidth, float maxHeight) +{ + const float minimumWidth = maxWidth / 2.0f; + float bestWidth = maxWidth; + float bestLineProportion = 0.0f; + + while (maxWidth > minimumWidth) + { + createLayout (text, maxWidth, maxHeight); + + if (getNumLines() < 2) + return; + + const float line1 = lines.getUnchecked (lines.size() - 1)->getLineBoundsX().getLength(); + const float line2 = lines.getUnchecked (lines.size() - 2)->getLineBoundsX().getLength(); + const float shortestLine = jmin (line1, line2); + const float prop = (shortestLine > 0) ? jmax (line1, line2) / shortestLine : 1.0f; + + if (prop > 0.9f) + return; + + if (prop > bestLineProportion) + { + bestLineProportion = prop; + bestWidth = maxWidth; + } + + maxWidth -= 10.0f; + } + + if (bestWidth != maxWidth) + createLayout (text, bestWidth, maxHeight); } //============================================================================== @@ -305,8 +357,8 @@ namespace TextLayoutHelpers { const Token& t = *tokens.getUnchecked (i); - Array newGlyphs; - Array xOffsets; + Array newGlyphs; + Array xOffsets; t.font.getGlyphPositions (getTrimmedEndIfNotAllWhitespace (t.text), newGlyphs, xOffsets); if (currentRun == nullptr) currentRun = new TextLayout::Run(); @@ -561,41 +613,6 @@ namespace TextLayoutHelpers }; } -//============================================================================== -void TextLayout::createLayoutWithBalancedLineLengths (const AttributedString& text, float maxWidth) -{ - const float minimumWidth = maxWidth / 2.0f; - float bestWidth = maxWidth; - float bestLineProportion = 0.0f; - - while (maxWidth > minimumWidth) - { - createLayout (text, maxWidth); - - if (getNumLines() < 2) - return; - - const float line1 = lines.getUnchecked (lines.size() - 1)->getLineBoundsX().getLength(); - const float line2 = lines.getUnchecked (lines.size() - 2)->getLineBoundsX().getLength(); - const float shortestLine = jmin (line1, line2); - const float prop = (shortestLine > 0) ? jmax (line1, line2) / shortestLine : 1.0f; - - if (prop > 0.9f) - return; - - if (prop > bestLineProportion) - { - bestLineProportion = prop; - bestWidth = maxWidth; - } - - maxWidth -= 10.0f; - } - - if (bestWidth != maxWidth) - createLayout (text, bestWidth); -} - //============================================================================== void TextLayout::createStandardLayout (const AttributedString& text) { @@ -603,18 +620,19 @@ void TextLayout::createStandardLayout (const AttributedString& text) l.createLayout (text, *this); } -void TextLayout::recalculateWidth (const AttributedString& text) +void TextLayout::recalculateSize (const AttributedString& text) { if (lines.size() > 0 && text.getReadingDirection() != AttributedString::rightToLeft) { - Range range (lines.getFirst()->getLineBoundsX()); + Rectangle bounds (lines.getFirst()->getLineBounds()); for (int i = lines.size(); --i > 0;) - range = range.getUnionWith (lines.getUnchecked(i)->getLineBoundsX()); + bounds = bounds.getUnion (lines.getUnchecked(i)->getLineBounds()); for (int i = lines.size(); --i >= 0;) - lines.getUnchecked(i)->lineOrigin.x -= range.getStart(); + lines.getUnchecked(i)->lineOrigin.x -= bounds.getX(); - width = range.getLength(); + width = bounds.getWidth(); + height = bounds.getHeight(); } } diff --git a/source/modules/juce_graphics/fonts/juce_TextLayout.h b/source/modules/juce_graphics/fonts/juce_TextLayout.h index b5f9eeadd..0ae8237d9 100644 --- a/source/modules/juce_graphics/fonts/juce_TextLayout.h +++ b/source/modules/juce_graphics/fonts/juce_TextLayout.h @@ -46,7 +46,7 @@ public: TextLayout (const TextLayout&); TextLayout& operator= (const TextLayout&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - TextLayout (TextLayout&& other) noexcept; + TextLayout (TextLayout&&) noexcept; TextLayout& operator= (TextLayout&&) noexcept; #endif @@ -57,7 +57,20 @@ public: /** Creates a layout from the given attributed string. This will replace any data that is currently stored in the layout. */ - void createLayout (const AttributedString& text, float maxWidth); + void createLayout (const AttributedString&, float maxWidth); + + /** Creates a layout from the given attributed string, given some size constraints. + This will replace any data that is currently stored in the layout. + */ + void createLayout (const AttributedString&, float maxWidth, float maxHeight); + + /** Creates a layout, attempting to choose a width which results in lines + of a similar length. + + This will be slower than the normal createLayout method, but produces a + tidier result. + */ + void createLayoutWithBalancedLineLengths (const AttributedString&, float maxWidth); /** Creates a layout, attempting to choose a width which results in lines of a similar length. @@ -65,13 +78,13 @@ public: This will be slower than the normal createLayout method, but produces a tidier result. */ - void createLayoutWithBalancedLineLengths (const AttributedString& text, float maxWidth); + void createLayoutWithBalancedLineLengths (const AttributedString&, float maxWidth, float maxHeight); /** Draws the layout within the specified area. The position of the text within the rectangle is controlled by the justification flags set in the original AttributedString that was used to create this layout. */ - void draw (Graphics& g, const Rectangle& area) const; + void draw (Graphics&, const Rectangle& area) const; //============================================================================== /** A positioned glyph. */ @@ -131,6 +144,12 @@ public: /** Returns the X position range which contains all the glyphs in this line. */ Range getLineBoundsX() const noexcept; + /** Returns the Y position range which contains all the glyphs in this line. */ + Range getLineBoundsY() const noexcept; + + /** Returns the smallest rectangle which contains all the glyphs in this line. */ + Rectangle getLineBounds() const noexcept; + OwnedArray runs; /**< The glyph-runs in this line. */ Range stringRange; /**< The character range that this line represents in the original string that was used to create it. */ @@ -147,7 +166,7 @@ public: float getWidth() const noexcept { return width; } /** Returns the maximum height of the content. */ - float getHeight() const noexcept; + float getHeight() const noexcept { return height; } /** Returns the number of lines in the layout. */ int getNumLines() const noexcept { return lines.size(); } @@ -157,19 +176,19 @@ public: /** Adds a line to the layout. The layout will take ownership of this line object and will delete it when it is no longer needed. */ - void addLine (Line* line); + void addLine (Line*); /** Pre-allocates space for the specified number of lines. */ void ensureStorageAllocated (int numLinesNeeded); private: OwnedArray lines; - float width; + float width, height; Justification justification; void createStandardLayout (const AttributedString&); bool createNativeLayout (const AttributedString&); - void recalculateWidth (const AttributedString&); + void recalculateSize (const AttributedString&); JUCE_LEAK_DETECTOR (TextLayout) }; diff --git a/source/modules/juce_graphics/geometry/juce_AffineTransform.cpp b/source/modules/juce_graphics/geometry/juce_AffineTransform.cpp index 59ebde3cf..85d6f4e8e 100644 --- a/source/modules/juce_graphics/geometry/juce_AffineTransform.cpp +++ b/source/modules/juce_graphics/geometry/juce_AffineTransform.cpp @@ -193,9 +193,9 @@ AffineTransform AffineTransform::sheared (const float shearX, const float shearY return AffineTransform (mat00 + shearX * mat10, mat01 + shearX * mat11, mat02 + shearX * mat12, - shearY * mat00 + mat10, - shearY * mat01 + mat11, - shearY * mat02 + mat12); + mat10 + shearY * mat00, + mat11 + shearY * mat01, + mat12 + shearY * mat02); } AffineTransform AffineTransform::verticalFlip (const float height) noexcept @@ -211,10 +211,10 @@ AffineTransform AffineTransform::inverted() const noexcept { determinant = 1.0 / determinant; - const float dst00 = (float) (mat11 * determinant); + const float dst00 = (float) ( mat11 * determinant); const float dst10 = (float) (-mat10 * determinant); const float dst01 = (float) (-mat01 * determinant); - const float dst11 = (float) (mat00 * determinant); + const float dst11 = (float) ( mat00 * determinant); return AffineTransform (dst00, dst01, -mat02 * dst00 - mat12 * dst01, dst10, dst11, -mat02 * dst10 - mat12 * dst11); @@ -258,5 +258,5 @@ bool AffineTransform::isOnlyTranslation() const noexcept float AffineTransform::getScaleFactor() const noexcept { - return (mat00 + mat11) / 2.0f; + return (std::abs (mat00) + std::abs (mat11)) / 2.0f; } diff --git a/source/modules/juce_graphics/geometry/juce_Path.cpp b/source/modules/juce_graphics/geometry/juce_Path.cpp index 5a6b2f866..204181a61 100644 --- a/source/modules/juce_graphics/geometry/juce_Path.cpp +++ b/source/modules/juce_graphics/geometry/juce_Path.cpp @@ -501,13 +501,20 @@ void Path::addRoundedRectangle (float x, float y, float w, float h, float cs) addRoundedRectangle (x, y, w, h, cs, cs); } -void Path::addTriangle (const float x1, const float y1, - const float x2, const float y2, - const float x3, const float y3) +void Path::addTriangle (float x1, float y1, + float x2, float y2, + float x3, float y3) { - startNewSubPath (x1, y1); - lineTo (x2, y2); - lineTo (x3, y3); + addTriangle (Point (x1, y1), + Point (x2, y2), + Point (x3, y3)); +} + +void Path::addTriangle (Point p1, Point p2, Point p3) +{ + startNewSubPath (p1); + lineTo (p2); + lineTo (p3); closeSubPath(); } @@ -649,6 +656,20 @@ void Path::addPieSegment (const float x, const float y, closeSubPath(); } +void Path::addPieSegment (Rectangle segmentBounds, + const float fromRadians, + const float toRadians, + const float innerCircleProportionalSize) +{ + addPieSegment (segmentBounds.getX(), + segmentBounds.getY(), + segmentBounds.getWidth(), + segmentBounds.getHeight(), + fromRadians, + toRadians, + innerCircleProportionalSize); +} + //============================================================================== void Path::addLineSegment (const Line& line, float lineThickness) { diff --git a/source/modules/juce_graphics/geometry/juce_Path.h b/source/modules/juce_graphics/geometry/juce_Path.h index ac319f96f..3dad5da1f 100644 --- a/source/modules/juce_graphics/geometry/juce_Path.h +++ b/source/modules/juce_graphics/geometry/juce_Path.h @@ -377,6 +377,18 @@ public: float x2, float y2, float x3, float y3); + /** Adds a triangle to the path. + + The triangle is added as a new closed sub-path. (Any currently open paths will be left open). + + Note that whether the vertices are specified in clockwise or anticlockwise + order will affect how the triangle is filled when it overlaps other + shapes (the winding order setting will affect this of course). + */ + void addTriangle (Point point1, + Point point2, + Point point3); + /** Adds a quadrilateral to the path. The quad is added as a new closed sub-path. (Any currently open paths will be left open). @@ -477,7 +489,6 @@ public: @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. - @see addArc */ void addPieSegment (float x, float y, @@ -486,6 +497,30 @@ public: float toRadians, float innerCircleProportionalSize); + /** 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). + + 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 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. + @see addArc + */ + void addPieSegment (Rectangle segmentBounds, + float fromRadians, + float toRadians, + float innerCircleProportionalSize); + /** Adds a line with a specified thickness. The line is added as a new closed sub-path. (Any currently open paths will be diff --git a/source/modules/juce_graphics/images/juce_ImageCache.cpp b/source/modules/juce_graphics/images/juce_ImageCache.cpp index 992ff328b..a806e48d9 100644 --- a/source/modules/juce_graphics/images/juce_ImageCache.cpp +++ b/source/modules/juce_graphics/images/juce_ImageCache.cpp @@ -110,7 +110,7 @@ public: unsigned int cacheTimeout; - juce_DeclareSingleton_SingleThreaded_Minimal (ImageCache::Pimpl); + juce_DeclareSingleton_SingleThreaded_Minimal (ImageCache::Pimpl) private: OwnedArray images; @@ -119,7 +119,7 @@ private: JUCE_DECLARE_NON_COPYABLE (Pimpl) }; -juce_ImplementSingleton_SingleThreaded (ImageCache::Pimpl); +juce_ImplementSingleton_SingleThreaded (ImageCache::Pimpl) //============================================================================== diff --git a/source/modules/juce_graphics/native/juce_RenderingHelpers.h b/source/modules/juce_graphics/native/juce_RenderingHelpers.h index 885cebd00..ad08e9f93 100644 --- a/source/modules/juce_graphics/native/juce_RenderingHelpers.h +++ b/source/modules/juce_graphics/native/juce_RenderingHelpers.h @@ -162,15 +162,6 @@ public: } //============================================================================== - void drawGlyph (RenderTargetType& target, const Font& font, const int glyphNumber, Point pos) - { - if (ReferenceCountedObjectPtr glyph = findOrCreateGlyph (font, glyphNumber)) - { - glyph->lastAccessCount = ++accessCounter; - glyph->draw (target, pos); - } - } - void reset() { const ScopedLock sl (lock); @@ -180,11 +171,14 @@ public: misses.set (0); } -private: - friend struct ContainerDeletePolicy; - ReferenceCountedArray glyphs; - Atomic accessCounter, hits, misses; - CriticalSection lock; + void drawGlyph (RenderTargetType& target, const Font& font, const int glyphNumber, Point pos) + { + if (ReferenceCountedObjectPtr glyph = findOrCreateGlyph (font, glyphNumber)) + { + glyph->lastAccessCount = ++accessCounter; + glyph->draw (target, pos); + } + } ReferenceCountedObjectPtr findOrCreateGlyph (const Font& font, int glyphNumber) { @@ -203,6 +197,12 @@ private: return g; } +private: + friend struct ContainerDeletePolicy; + ReferenceCountedArray glyphs; + Atomic accessCounter, hits, misses; + CriticalSection lock; + CachedGlyphType* findExistingGlyph (const Font& font, int glyphNumber) const { for (int i = 0; i < glyphs.size(); ++i) @@ -302,12 +302,10 @@ public: } Font font; + ScopedPointer edgeTable; int glyph, lastAccessCount; bool snapToIntegerCoordinate; -private: - ScopedPointer edgeTable; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedGlyphEdgeTable) }; diff --git a/source/modules/juce_graphics/native/juce_android_Fonts.cpp b/source/modules/juce_graphics/native/juce_android_Fonts.cpp index 29702a20b..e08ef2a01 100644 --- a/source/modules/juce_graphics/native/juce_android_Fonts.cpp +++ b/source/modules/juce_graphics/native/juce_android_Fonts.cpp @@ -138,22 +138,47 @@ public: { JNIEnv* const env = getEnv(); - const bool isBold = style.contains ("Bold"); - const bool isItalic = style.contains ("Italic"); + // First check whether there's an embedded asset with this font name: + typeface = GlobalRef (android.activity.callObjectMethod (JuceAppActivity.getTypeFaceFromAsset, + javaString ("fonts/" + name).get())); - File fontFile (getFontFile (name, style)); + if (typeface.get() == nullptr) + { + const bool isBold = style.contains ("Bold"); + const bool isItalic = style.contains ("Italic"); + + File fontFile (getFontFile (name, style)); - if (! fontFile.exists()) - fontFile = findFontFile (name, isBold, isItalic); + if (! fontFile.exists()) + fontFile = findFontFile (name, isBold, isItalic); - if (fontFile.exists()) - typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromFile, - javaString (fontFile.getFullPathName()).get())); - else - typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.create, - javaString (getName()).get(), - (isBold ? 1 : 0) + (isItalic ? 2 : 0))); + if (fontFile.exists()) + typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromFile, + javaString (fontFile.getFullPathName()).get())); + else + typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.create, + javaString (getName()).get(), + (isBold ? 1 : 0) + (isItalic ? 2 : 0))); + } + initialise (env); + } + + AndroidTypeface (const void* data, size_t size) + : Typeface (String(), String()) + { + JNIEnv* const env = getEnv(); + + LocalRef bytes (env->NewByteArray (size)); + env->SetByteArrayRegion (bytes, 0, size, (const jbyte*) data); + + typeface = GlobalRef (android.activity.callObjectMethod (JuceAppActivity.getTypeFaceFromByteArray, bytes.get())); + + initialise (env); + } + + void initialise (JNIEnv* const env) + { rect = GlobalRef (env->NewObject (RectClass, RectClass.constructor, 0, 0, 0, 0)); paint = GlobalRef (GraphicsHelpers::createPaint (Graphics::highResamplingQuality)); @@ -315,10 +340,9 @@ Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) return new AndroidTypeface (font); } -Typeface::Ptr Typeface::createSystemTypefaceFor (const void*, size_t) +Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t size) { - jassertfalse; // not yet implemented! - return nullptr; + return new AndroidTypeface (data, size); } void Typeface::scanFolderForFonts (const File&) diff --git a/source/modules/juce_graphics/native/juce_freetype_Fonts.cpp b/source/modules/juce_graphics/native/juce_freetype_Fonts.cpp index 43a6436e9..d124eafd9 100644 --- a/source/modules/juce_graphics/native/juce_freetype_Fonts.cpp +++ b/source/modules/juce_graphics/native/juce_freetype_Fonts.cpp @@ -224,7 +224,7 @@ public: sansSerif.addIfNotAlreadyThere (faces.getUnchecked(i)->family); } - juce_DeclareSingleton_SingleThreaded_Minimal (FTTypefaceList); + juce_DeclareSingleton_SingleThreaded_Minimal (FTTypefaceList) private: FTLibWrapper::Ptr library; diff --git a/source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h b/source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h index bf26ce8ee..9beefef00 100644 --- a/source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h +++ b/source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h @@ -77,7 +77,6 @@ private: const CGFloat flipHeight; float targetScale; CGColorSpaceRef rgbColourSpace, greyColourSpace; - CGFunctionCallbacks gradientCallbacks; mutable Rectangle lastClipRect; mutable bool lastClipRectIsValid; @@ -87,24 +86,17 @@ private: SavedState (const SavedState&); ~SavedState(); - void setFill (const FillType& newFill); - CGShadingRef getShading (CoreGraphicsContext& owner); - - static void gradientCallback (void* info, const CGFloat* inData, CGFloat* outData); + void setFill (const FillType&); FillType fillType; Font font; CGFontRef fontRef; CGAffineTransform fontTransform; - - private: - CGShadingRef shading; - HeapBlock gradientLookupTable; - int numGradientLookupEntries; + CGGradientRef gradient; }; - ScopedPointer state; - OwnedArray stateStack; + ScopedPointer state; + OwnedArray stateStack; void drawGradient(); void createPath (const Path&) const; diff --git a/source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm b/source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm index 44e344f16..ac7f27a04 100644 --- a/source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm +++ b/source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm @@ -25,7 +25,7 @@ #include "juce_mac_CoreGraphicsContext.h" //============================================================================== -class CoreGraphicsImage : public ImagePixelData +class CoreGraphicsImage : public ImagePixelData { public: CoreGraphicsImage (const Image::PixelFormat format, const int w, const int h, const bool clearImage) @@ -174,13 +174,11 @@ CoreGraphicsContext::CoreGraphicsContext (CGContextRef c, const float h, const f CGContextRetain (context); CGContextSaveGState(context); CGContextSetShouldSmoothFonts (context, true); + CGContextSetAllowsFontSmoothing (context, true); CGContextSetShouldAntialias (context, true); CGContextSetBlendMode (context, kCGBlendModeNormal); rgbColourSpace = CGColorSpaceCreateDeviceRGB(); greyColourSpace = CGColorSpaceCreateDeviceGray(); - gradientCallbacks.version = 0; - gradientCallbacks.evaluate = SavedState::gradientCallback; - gradientCallbacks.releaseInfo = 0; setFont (Font()); } @@ -218,8 +216,6 @@ float CoreGraphicsContext::getPhysicalPixelScaleFactor() const CGAffineTransform t = CGContextGetCTM (context); return targetScale * (float) (juce_hypot (t.a, t.c) + juce_hypot (t.b, t.d)) / 2.0f; - -// return targetScale * (float) (t.a + t.d) / 2.0f; } bool CoreGraphicsContext::clipToRectangle (const Rectangle& r) @@ -248,19 +244,17 @@ bool CoreGraphicsContext::clipToRectangleListWithoutTest (const RectangleList(); return false; } - else - { - const size_t numRects = (size_t) clipRegion.getNumRectangles(); - HeapBlock rects (numRects); - int i = 0; - for (const Rectangle* r = clipRegion.begin(), * const e = clipRegion.end(); r != e; ++r) - rects[i++] = CGRectMake (r->getX(), flipHeight - r->getBottom(), r->getWidth(), r->getHeight()); + const size_t numRects = (size_t) clipRegion.getNumRectangles(); + HeapBlock rects (numRects); - CGContextClipToRects (context, rects, numRects); - lastClipRectIsValid = false; - return true; - } + int i = 0; + for (const Rectangle* r = clipRegion.begin(), * const e = clipRegion.end(); r != e; ++r) + rects[i++] = CGRectMake (r->getX(), flipHeight - r->getBottom(), r->getWidth(), r->getHeight()); + + CGContextClipToRects (context, rects, numRects); + lastClipRectIsValid = false; + return true; } bool CoreGraphicsContext::clipToRectangleList (const RectangleList& clipRegion) @@ -507,7 +501,7 @@ void CoreGraphicsContext::drawImage (const Image& sourceImage, const AffineTrans #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 // There's a bug in CGContextDrawTiledImage that makes it incredibly slow // if it's doing a transformation - it's quicker to just draw lots of images manually - if (CGContextDrawTiledImage != 0 && transform.isOnlyTranslation()) + if (&CGContextDrawTiledImage != 0 && transform.isOnlyTranslation()) CGContextDrawTiledImage (context, imageRect, image); else #endif @@ -519,7 +513,7 @@ void CoreGraphicsContext::drawImage (const Image& sourceImage, const AffineTrans while (x > clip.origin.x) x -= iw; while (y > clip.origin.y) y -= ih; - const int right = (int) (clip.origin.x + clip.size.width); + const int right = (int) (clip.origin.x + clip.size.width); const int bottom = (int) (clip.origin.y + clip.size.height); while (y < bottom) @@ -600,7 +594,7 @@ void CoreGraphicsContext::setFont (const Font& newFont) state->fontRef = 0; state->font = newFont; - if (OSXTypeface* osxTypeface = dynamic_cast (state->font.getTypeface())) + if (OSXTypeface* osxTypeface = dynamic_cast (state->font.getTypeface())) { state->fontRef = osxTypeface->fontRef; CGContextSetFont (context, state->fontRef); @@ -678,97 +672,85 @@ bool CoreGraphicsContext::drawTextLayout (const AttributedString& text, const Re } CoreGraphicsContext::SavedState::SavedState() - : font (1.0f), fontRef (0), fontTransform (CGAffineTransformIdentity), - shading (0), numGradientLookupEntries (0) + : font (1.0f), fontRef (0), fontTransform (CGAffineTransformIdentity), gradient (0) { } CoreGraphicsContext::SavedState::SavedState (const SavedState& other) : fillType (other.fillType), font (other.font), fontRef (other.fontRef), - fontTransform (other.fontTransform), shading (0), - gradientLookupTable ((size_t) other.numGradientLookupEntries), - numGradientLookupEntries (other.numGradientLookupEntries) + fontTransform (other.fontTransform), gradient (other.gradient) { - memcpy (gradientLookupTable, other.gradientLookupTable, sizeof (PixelARGB) * (size_t) numGradientLookupEntries); + if (gradient != 0) + CGGradientRetain (gradient); } CoreGraphicsContext::SavedState::~SavedState() { - if (shading != 0) - CGShadingRelease (shading); + if (gradient != 0) + CGGradientRelease (gradient); } void CoreGraphicsContext::SavedState::setFill (const FillType& newFill) { fillType = newFill; - if (fillType.isGradient() && shading != 0) + if (gradient != 0) { - CGShadingRelease (shading); - shading = 0; + CGGradientRelease (gradient); + gradient = 0; } } -CGShadingRef CoreGraphicsContext::SavedState::getShading (CoreGraphicsContext& owner) +static CGGradientRef createGradient (const ColourGradient& g, CGColorSpaceRef colourSpace) { - if (shading == 0) - { - ColourGradient& g = *(fillType.gradient); - numGradientLookupEntries = g.createLookupTable (fillType.transform, gradientLookupTable) - 1; - - CGFunctionRef function = CGFunctionCreate (this, 1, 0, 4, 0, &(owner.gradientCallbacks)); - CGPoint p1 (convertToCGPoint (g.point1)); + const int numColours = g.getNumColours(); + CGFloat* const data = (CGFloat*) alloca ((size_t) numColours * 5 * sizeof (CGFloat)); + CGFloat* const locations = data; + CGFloat* const components = data + numColours; + CGFloat* comps = components; - if (g.isRadial) - { - shading = CGShadingCreateRadial (owner.rgbColourSpace, p1, 0, - p1, g.point1.getDistanceFrom (g.point2), - function, true, true); - } - else - { - shading = CGShadingCreateAxial (owner.rgbColourSpace, p1, - convertToCGPoint (g.point2), - function, true, true); - } - - CGFunctionRelease (function); + for (int i = 0; i < numColours; ++i) + { + const Colour colour (g.getColour (i)); + *comps++ = (CGFloat) colour.getFloatRed(); + *comps++ = (CGFloat) colour.getFloatGreen(); + *comps++ = (CGFloat) colour.getFloatBlue(); + *comps++ = (CGFloat) colour.getFloatAlpha(); + locations[i] = (CGFloat) g.getColourPosition (i); } - return shading; -} - -void CoreGraphicsContext::SavedState::gradientCallback (void* info, const CGFloat* inData, CGFloat* outData) -{ - const SavedState* const s = static_cast (info); - - const int index = roundToInt (s->numGradientLookupEntries * inData[0]); - PixelARGB colour (s->gradientLookupTable [jlimit (0, s->numGradientLookupEntries, index)]); - colour.unpremultiply(); - - outData[0] = colour.getRed() / 255.0f; - outData[1] = colour.getGreen() / 255.0f; - outData[2] = colour.getBlue() / 255.0f; - outData[3] = colour.getAlpha() / 255.0f; + return CGGradientCreateWithColorComponents (colourSpace, components, locations, (size_t) numColours); } void CoreGraphicsContext::drawGradient() { flip(); applyTransform (state->fillType.transform); - - CGContextSetInterpolationQuality (context, kCGInterpolationDefault); // (This is required for 10.4, where there's a crash if - // you draw a gradient with high quality interp enabled). CGContextSetAlpha (context, state->fillType.getOpacity()); - CGContextDrawShading (context, state->getShading (*this)); + + const ColourGradient& g = *state->fillType.gradient; + + CGPoint p1 (convertToCGPoint (g.point1)); + CGPoint p2 (convertToCGPoint (g.point2)); + + state->fillType.transform.transformPoints (p1.x, p1.y, p2.x, p2.y); + + if (state->gradient == 0) + state->gradient = createGradient (g, rgbColourSpace); + + if (g.isRadial) + CGContextDrawRadialGradient (context, state->gradient, p1, 0, p1, g.point1.getDistanceFrom (g.point2), + kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); + else + CGContextDrawLinearGradient (context, state->gradient, p1, p2, + kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); } void CoreGraphicsContext::createPath (const Path& path) const { CGContextBeginPath (context); - Path::Iterator i (path); - while (i.next()) + for (Path::Iterator i (path); i.next();) { switch (i.elementType) { @@ -785,9 +767,8 @@ void CoreGraphicsContext::createPath (const Path& path) const void CoreGraphicsContext::createPath (const Path& path, const AffineTransform& transform) const { CGContextBeginPath (context); - Path::Iterator i (path); - while (i.next()) + for (Path::Iterator i (path); i.next();) { switch (i.elementType) { @@ -917,7 +898,7 @@ CGImageRef juce_createCoreGraphicsImage (const Image& juceImage, CGColorSpaceRef CGContextRef juce_getImageContext (const Image& image) { - if (CoreGraphicsImage* const cgi = dynamic_cast (image.getPixelData())) + if (CoreGraphicsImage* const cgi = dynamic_cast (image.getPixelData())) return cgi->context; jassertfalse; diff --git a/source/modules/juce_graphics/native/juce_mac_CoreGraphicsHelpers.h b/source/modules/juce_graphics/native/juce_mac_CoreGraphicsHelpers.h index ca6f69926..bc9e66bd3 100644 --- a/source/modules/juce_graphics/native/juce_mac_CoreGraphicsHelpers.h +++ b/source/modules/juce_graphics/native/juce_mac_CoreGraphicsHelpers.h @@ -30,25 +30,25 @@ namespace { template - Rectangle convertToRectInt (const RectType& r) noexcept + Rectangle convertToRectInt (RectType r) noexcept { return Rectangle ((int) r.origin.x, (int) r.origin.y, (int) r.size.width, (int) r.size.height); } template - Rectangle convertToRectFloat (const RectType& r) noexcept + Rectangle convertToRectFloat (RectType r) noexcept { return Rectangle (r.origin.x, r.origin.y, r.size.width, r.size.height); } template - CGRect convertToCGRect (const RectType& r) noexcept + CGRect convertToCGRect (RectType r) noexcept { return CGRectMake ((CGFloat) r.getX(), (CGFloat) r.getY(), (CGFloat) r.getWidth(), (CGFloat) r.getHeight()); } template - CGPoint convertToCGPoint (const PointType& p) noexcept + CGPoint convertToCGPoint (PointType p) noexcept { return CGPointMake ((CGFloat) p.x, (CGFloat) p.y); } diff --git a/source/modules/juce_graphics/native/juce_mac_Fonts.mm b/source/modules/juce_graphics/native/juce_mac_Fonts.mm index 81a29a804..3298e9ef1 100644 --- a/source/modules/juce_graphics/native/juce_mac_Fonts.mm +++ b/source/modules/juce_graphics/native/juce_mac_Fonts.mm @@ -385,7 +385,7 @@ namespace CoreTextTypeLayout static void createLayout (TextLayout& glyphLayout, const AttributedString& text) { - const CGFloat boundsHeight = 1.0e6f; + const CGFloat boundsHeight = glyphLayout.getHeight(); CTFrameRef frame = createCTFrame (text, CGRectMake (0, 0, glyphLayout.getWidth(), boundsHeight)); CFArrayRef lines = CTFrameGetLines (frame); diff --git a/source/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp b/source/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp index 83e3c9d94..fd2c7a703 100644 --- a/source/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp +++ b/source/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp @@ -311,6 +311,13 @@ namespace DirectWriteTypeLayout setTextFormatProperties (text, dwTextFormat); + { + DWRITE_TRIMMING trimming = { DWRITE_TRIMMING_GRANULARITY_CHARACTER, 0, 0 }; + ComSmartPtr trimmingSign; + hr = directWriteFactory->CreateEllipsisTrimmingSign (dwTextFormat, trimmingSign.resetAndGetPointerAddress()); + hr = dwTextFormat->SetTrimming (&trimming, trimmingSign); + } + const int textLen = text.getText().length(); hr = directWriteFactory->CreateTextLayout (text.getText().toWideCharPointer(), textLen, dwTextFormat, @@ -344,7 +351,8 @@ namespace DirectWriteTypeLayout ComSmartPtr dwTextLayout; - if (! setupLayout (text, layout.getWidth(), 1.0e7f, renderTarget, directWriteFactory, fontCollection, dwTextLayout)) + if (! setupLayout (text, layout.getWidth(), layout.getHeight(), renderTarget, + directWriteFactory, fontCollection, dwTextLayout)) return; UINT32 actualLineCount = 0; @@ -374,7 +382,8 @@ namespace DirectWriteTypeLayout { ComSmartPtr dwTextLayout; - if (setupLayout (text, area.getWidth(), area.getHeight(), renderTarget, directWriteFactory, fontCollection, dwTextLayout)) + if (setupLayout (text, area.getWidth(), area.getHeight(), renderTarget, + directWriteFactory, fontCollection, dwTextLayout)) { ComSmartPtr d2dBrush; renderTarget->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF (0.0f, 0.0f, 0.0f, 1.0f)), diff --git a/source/modules/juce_gui_basics/buttons/juce_TextButton.h b/source/modules/juce_gui_basics/buttons/juce_TextButton.h index b9093b27b..31c477ba7 100644 --- a/source/modules/juce_gui_basics/buttons/juce_TextButton.h +++ b/source/modules/juce_gui_basics/buttons/juce_TextButton.h @@ -102,7 +102,7 @@ public: private: #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // Note that this method has been removed - instead, see LookAndFeel::getTextButtonWidthToFitText() + // Note that this method has been removed - instead, see LookAndFeel::getTextButtonFont() virtual int getFont() { return 0; } #endif diff --git a/source/modules/juce_gui_basics/components/juce_Component.cpp b/source/modules/juce_gui_basics/components/juce_Component.cpp index 7cbe59fb9..d1a93182d 100644 --- a/source/modules/juce_gui_basics/components/juce_Component.cpp +++ b/source/modules/juce_gui_basics/components/juce_Component.cpp @@ -784,6 +784,7 @@ public: validArea.clear(); } + if (! validArea.containsRectangle (compBounds)) { Graphics imG (image); LowLevelGraphicsContext& lg = imG.getInternalContext(); @@ -791,18 +792,15 @@ public: for (const Rectangle* i = validArea.begin(), * const e = validArea.end(); i != e; ++i) lg.excludeClipRectangle (*i); - if (! lg.isClipEmpty()) + if (! owner.isOpaque()) { - if (! owner.isOpaque()) - { - lg.setFill (Colours::transparentBlack); - lg.fillRect (imageBounds, true); - lg.setFill (Colours::black); - } - - lg.addTransform (AffineTransform::scale (scale)); - owner.paintEntireComponent (imG, true); + lg.setFill (Colours::transparentBlack); + lg.fillRect (imageBounds, true); + lg.setFill (Colours::black); } + + lg.addTransform (AffineTransform::scale (scale)); + owner.paintEntireComponent (imG, true); } validArea = imageBounds; @@ -2311,16 +2309,15 @@ void Component::internalModalInputAttempt() //============================================================================== void Component::postCommandMessage (const int commandId) { - class CustomCommandMessage : public CallbackMessage + struct CustomCommandMessage : public CallbackMessage { - public: CustomCommandMessage (Component* const c, const int command) : target (c), commandId (command) {} void messageCallback() override { - if (target.get() != nullptr) // (get() required for VS2003 bug) - target->handleCommandMessage (commandId); + if (Component* c = target.get()) + c->handleCommandMessage (commandId); } private: diff --git a/source/modules/juce_gui_basics/components/juce_Component.h b/source/modules/juce_gui_basics/components/juce_Component.h index edc1957a3..8004fd633 100644 --- a/source/modules/juce_gui_basics/components/juce_Component.h +++ b/source/modules/juce_gui_basics/components/juce_Component.h @@ -775,7 +775,13 @@ public: */ void removeAllChildren(); - /** Removes all this component's children, and deletes them. + /** Removes and deletes all of this component's children. + My advice is to avoid this method! It's an old function that is only kept here for + backwards-compatibility with legacy code, and should be viewed with extreme + suspicion by anyone attempting to write modern C++. In almost all cases, it's much + smarter to manage the lifetimes of your child components via modern RAII techniques + such as simply making them member variables, or using ScopedPointer, OwnedArray, etc + to manage their lifetimes appropriately. @see removeAllChildren */ void deleteAllChildren(); diff --git a/source/modules/juce_gui_basics/components/juce_ModalComponentManager.cpp b/source/modules/juce_gui_basics/components/juce_ModalComponentManager.cpp index a6e7b0711..12b81df06 100644 --- a/source/modules/juce_gui_basics/components/juce_ModalComponentManager.cpp +++ b/source/modules/juce_gui_basics/components/juce_ModalComponentManager.cpp @@ -89,7 +89,7 @@ ModalComponentManager::~ModalComponentManager() clearSingletonInstance(); } -juce_ImplementSingleton_SingleThreaded (ModalComponentManager); +juce_ImplementSingleton_SingleThreaded (ModalComponentManager) //============================================================================== diff --git a/source/modules/juce_gui_basics/components/juce_ModalComponentManager.h b/source/modules/juce_gui_basics/components/juce_ModalComponentManager.h index b21421ebf..1b927e121 100644 --- a/source/modules/juce_gui_basics/components/juce_ModalComponentManager.h +++ b/source/modules/juce_gui_basics/components/juce_ModalComponentManager.h @@ -120,7 +120,7 @@ public: #endif //============================================================================== - juce_DeclareSingleton_SingleThreaded_Minimal (ModalComponentManager); + juce_DeclareSingleton_SingleThreaded_Minimal (ModalComponentManager) protected: /** Creates a ModalComponentManager. diff --git a/source/modules/juce_gui_basics/drawables/juce_DrawableImage.cpp b/source/modules/juce_gui_basics/drawables/juce_DrawableImage.cpp index 7c29dee04..d609011a1 100644 --- a/source/modules/juce_gui_basics/drawables/juce_DrawableImage.cpp +++ b/source/modules/juce_gui_basics/drawables/juce_DrawableImage.cpp @@ -37,6 +37,7 @@ DrawableImage::DrawableImage (const DrawableImage& other) overlayColour (other.overlayColour), bounds (other.bounds) { + setBounds (other.getBounds()); } DrawableImage::~DrawableImage() diff --git a/source/modules/juce_gui_basics/drawables/juce_SVGParser.cpp b/source/modules/juce_gui_basics/drawables/juce_SVGParser.cpp index cfb6b7b5f..5e236f03c 100644 --- a/source/modules/juce_gui_basics/drawables/juce_SVGParser.cpp +++ b/source/modules/juce_gui_basics/drawables/juce_SVGParser.cpp @@ -372,7 +372,7 @@ private: if (tag == "line") return parseLine (xml); if (tag == "polyline") return parsePolygon (xml, true); if (tag == "polygon") return parsePolygon (xml, false); - if (tag == "text") return parseText (xml); + if (tag == "text") return parseText (xml, true); if (tag == "switch") return parseSwitch (xml); if (tag == "style") parseCSSStyle (xml); @@ -804,8 +804,16 @@ private: } //============================================================================== - Drawable* parseText (const XmlPath& xml) + Drawable* parseText (const XmlPath& xml, bool shouldParseTransform) { + if (shouldParseTransform && xml->hasAttribute ("transform")) + { + SVGState newState (*this); + newState.addTransform (xml); + + return newState.parseText (xml, false); + } + Array xCoords, yCoords, dxCoords, dyCoords; getCoordList (xCoords, getInheritedAttribute (xml, "x"), true, true); @@ -813,28 +821,59 @@ private: getCoordList (dxCoords, getInheritedAttribute (xml, "dx"), true, true); getCoordList (dyCoords, getInheritedAttribute (xml, "dy"), true, false); + const Font font (getFont (xml)); + const String anchorStr = getStyleAttribute(xml, "text-anchor"); - //xxx not done text yet! - + DrawableComposite* dc = new DrawableComposite(); + setDrawableID (*dc, xml); forEachXmlChildElement (*xml, e) { if (e->isTextElement()) { - const String text (e->getText()); + const String text (e->getText().trim()); + + DrawableText* dt = new DrawableText(); + dc->addAndMakeVisible (dt); + + dt->setText (text); + dt->setFont (font, true); + dt->setTransform (transform); + + int i = 0; + dt->setColour (parseColour (getStyleAttribute (xml, "fill"), i, Colours::black) + .withMultipliedAlpha (getStyleAttribute (xml, "fill-opacity", "1").getFloatValue())); + + Rectangle bounds (xCoords[0], yCoords[0] - font.getAscent(), + font.getStringWidthFloat (text), font.getHeight()); - Path path; - Drawable* s = parseShape (xml.getChild (e), path); - delete s; // xxx not finished! + if (anchorStr == "middle") bounds.setX (bounds.getX() - bounds.getWidth() / 2.0f); + else if (anchorStr == "end") bounds.setX (bounds.getX() - bounds.getWidth()); + + dt->setBoundingBox (bounds); } else if (e->hasTagNameIgnoringNamespace ("tspan")) { - Drawable* s = parseText (xml.getChild (e)); - delete s; // xxx not finished! + dc->addAndMakeVisible (parseText (xml.getChild (e), true)); } } - return nullptr; + return dc; + } + + Font getFont (const XmlPath& xml) const + { + const float fontSize = getCoordLength (getStyleAttribute (xml, "font-size"), 1.0f); + + int style = getStyleAttribute (xml, "font-style").containsIgnoreCase ("italic") ? Font::italic : Font::plain; + + if (getStyleAttribute (xml, "font-weight").containsIgnoreCase ("bold")) + style |= Font::bold; + + const String family (getStyleAttribute (xml, "font-family")); + + return family.isEmpty() ? Font (fontSize, style) + : Font (family, fontSize, style); } //============================================================================== @@ -1157,9 +1196,9 @@ private: tokens.removeEmptyStrings (true); - float numbers [6]; + float numbers[6]; - for (int i = 0; i < 6; ++i) + for (int i = 0; i < numElementsInArray (numbers); ++i) numbers[i] = tokens[i].getFloatValue(); AffineTransform trans; @@ -1171,33 +1210,23 @@ private: } else if (t.startsWithIgnoreCase ("translate")) { - jassert (tokens.size() == 2); trans = AffineTransform::translation (numbers[0], numbers[1]); } else if (t.startsWithIgnoreCase ("scale")) { - if (tokens.size() == 1) - trans = AffineTransform::scale (numbers[0]); - else - trans = AffineTransform::scale (numbers[0], numbers[1]); + trans = AffineTransform::scale (numbers[0], numbers[tokens.size() > 1 ? 1 : 0]); } else if (t.startsWithIgnoreCase ("rotate")) { - if (tokens.size() != 3) - trans = AffineTransform::rotation (numbers[0] / (180.0f / float_Pi)); - else - trans = AffineTransform::rotation (numbers[0] / (180.0f / float_Pi), - numbers[1], numbers[2]); + trans = AffineTransform::rotation (numbers[0] / (180.0f / float_Pi), numbers[1], numbers[2]); } else if (t.startsWithIgnoreCase ("skewX")) { - trans = AffineTransform (1.0f, std::tan (numbers[0] * (float_Pi / 180.0f)), 0.0f, - 0.0f, 1.0f, 0.0f); + trans = AffineTransform::shear (std::tan (numbers[0] * (float_Pi / 180.0f)), 0.0f); } else if (t.startsWithIgnoreCase ("skewY")) { - trans = AffineTransform (1.0f, 0.0f, 0.0f, - std::tan (numbers[0] * (float_Pi / 180.0f)), 1.0f, 0.0f); + trans = AffineTransform::shear (0.0f, std::tan (numbers[0] * (float_Pi / 180.0f))); } result = trans.followedBy (result); @@ -1218,8 +1247,8 @@ private: const double midX = (x1 - x2) * 0.5; const double midY = (y1 - y2) * 0.5; - const double cosAngle = cos (angle); - const double sinAngle = sin (angle); + const double cosAngle = std::cos (angle); + const double sinAngle = std::sin (angle); const double xp = cosAngle * midX + sinAngle * midY; const double yp = cosAngle * midY - sinAngle * midX; const double xp2 = xp * xp; diff --git a/source/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp b/source/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp index 71c148d31..e00ef470f 100644 --- a/source/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp +++ b/source/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp @@ -99,7 +99,7 @@ bool FileChooser::showDialog (const int flags, FilePreviewComponent* const previ #if JUCE_WINDOWS if (useNativeDialogBox && ! (selectsFiles && selectsDirectories)) #elif JUCE_MAC || JUCE_LINUX - if (useNativeDialogBox && (previewComp == nullptr)) + if (useNativeDialogBox) #else if (false) #endif diff --git a/source/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp b/source/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp index a2cad464b..ebec9566f 100644 --- a/source/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp +++ b/source/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp @@ -152,6 +152,8 @@ void FileSearchPathListComponent::returnKeyPressed (int row) path.add (chooser.getResult(), row); changed(); } + #else + (void) row; #endif } diff --git a/source/modules/juce_gui_basics/keyboard/juce_TextInputTarget.h b/source/modules/juce_gui_basics/keyboard/juce_TextInputTarget.h index 9770f5dc3..19a33b3db 100644 --- a/source/modules/juce_gui_basics/keyboard/juce_TextInputTarget.h +++ b/source/modules/juce_gui_basics/keyboard/juce_TextInputTarget.h @@ -79,6 +79,7 @@ public: { textKeyboard = 0, numericKeyboard, + decimalKeyboard, urlKeyboard, emailAddressKeyboard, phoneNumberKeyboard diff --git a/source/modules/juce_gui_basics/layout/juce_AnimatedPosition.h b/source/modules/juce_gui_basics/layout/juce_AnimatedPosition.h index dac6ce174..2f82e03cf 100644 --- a/source/modules/juce_gui_basics/layout/juce_AnimatedPosition.h +++ b/source/modules/juce_gui_basics/layout/juce_AnimatedPosition.h @@ -94,7 +94,7 @@ public: */ void endDrag() { - startTimer (1000 / 60); + startTimerHz (60); } /** Called outside of a drag operation to cause a nudge in the specified direction. @@ -102,7 +102,7 @@ public: */ void nudge (double deltaFromCurrentPosition) { - startTimer (100); + startTimerHz (10); moveTo (position + deltaFromCurrentPosition); } @@ -197,7 +197,7 @@ private: if (behaviour.isStopped (newPos)) stopTimer(); else - startTimer (1000 / 60); + startTimerHz (60); setPositionAndSendChange (newPos); } diff --git a/source/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp b/source/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp index 7c26c99af..3507fc839 100644 --- a/source/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp +++ b/source/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp @@ -25,9 +25,7 @@ class ComponentAnimator::AnimationTask { public: - AnimationTask (Component* const comp) noexcept : component (comp) - { - } + AnimationTask (Component* c) noexcept : component (c) {} void reset (const Rectangle& finalBounds, float finalAlpha, @@ -65,8 +63,8 @@ public: bool useTimeslice (const int elapsed) { - if (Component* const c = proxy != nullptr ? static_cast (proxy) - : static_cast (component)) + if (Component* const c = proxy != nullptr ? static_cast (proxy) + : static_cast (component)) { msElapsed += elapsed; double newProgress = msElapsed / (double) msTotal; @@ -149,7 +147,10 @@ public: else jassertfalse; // seem to be trying to animate a component that's not visible.. - image = c.createComponentSnapshot (c.getLocalBounds(), false, getDesktopScaleFactor()); + const float scale = (float) Desktop::getInstance().getDisplays() + .getDisplayContaining (getScreenBounds().getCentre()).scale; + + image = c.createComponentSnapshot (c.getLocalBounds(), false, scale); setVisible (true); toBehind (&c); @@ -189,14 +190,8 @@ private: }; //============================================================================== -ComponentAnimator::ComponentAnimator() - : lastTime (0) -{ -} - -ComponentAnimator::~ComponentAnimator() -{ -} +ComponentAnimator::ComponentAnimator() : lastTime (0) {} +ComponentAnimator::~ComponentAnimator() {} //============================================================================== ComponentAnimator::AnimationTask* ComponentAnimator::findTaskFor (Component* const component) const noexcept @@ -236,7 +231,7 @@ void ComponentAnimator::animateComponent (Component* const component, if (! isTimerRunning()) { lastTime = Time::getMillisecondCounter(); - startTimer (1000 / 50); + startTimerHz (50); } } } diff --git a/source/modules/juce_gui_basics/layout/juce_ComponentBuilder.cpp b/source/modules/juce_gui_basics/layout/juce_ComponentBuilder.cpp index 433ddf8d6..e510e8361 100644 --- a/source/modules/juce_gui_basics/layout/juce_ComponentBuilder.cpp +++ b/source/modules/juce_gui_basics/layout/juce_ComponentBuilder.cpp @@ -200,12 +200,12 @@ void ComponentBuilder::valueTreeChildAdded (ValueTree& tree, ValueTree&) ComponentBuilderHelpers::updateComponent (*this, tree); } -void ComponentBuilder::valueTreeChildRemoved (ValueTree& tree, ValueTree&) +void ComponentBuilder::valueTreeChildRemoved (ValueTree& tree, ValueTree&, int) { ComponentBuilderHelpers::updateComponent (*this, tree); } -void ComponentBuilder::valueTreeChildOrderChanged (ValueTree& tree) +void ComponentBuilder::valueTreeChildOrderChanged (ValueTree& tree, int, int) { ComponentBuilderHelpers::updateComponent (*this, tree); } diff --git a/source/modules/juce_gui_basics/layout/juce_ComponentBuilder.h b/source/modules/juce_gui_basics/layout/juce_ComponentBuilder.h index c524111f4..c5557deca 100644 --- a/source/modules/juce_gui_basics/layout/juce_ComponentBuilder.h +++ b/source/modules/juce_gui_basics/layout/juce_ComponentBuilder.h @@ -235,8 +235,8 @@ private: void valueTreePropertyChanged (ValueTree&, const Identifier&) override; void valueTreeChildAdded (ValueTree&, ValueTree&) override; - void valueTreeChildRemoved (ValueTree&, ValueTree&) override; - void valueTreeChildOrderChanged (ValueTree&) override; + void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override; + void valueTreeChildOrderChanged (ValueTree&, int, int) override; void valueTreeParentChanged (ValueTree&) override; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentBuilder) diff --git a/source/modules/juce_gui_basics/layout/juce_ConcertinaPanel.cpp b/source/modules/juce_gui_basics/layout/juce_ConcertinaPanel.cpp index 21d44cc7d..cee425575 100644 --- a/source/modules/juce_gui_basics/layout/juce_ConcertinaPanel.cpp +++ b/source/modules/juce_gui_basics/layout/juce_ConcertinaPanel.cpp @@ -282,6 +282,11 @@ ConcertinaPanel::ConcertinaPanel() ConcertinaPanel::~ConcertinaPanel() {} +int ConcertinaPanel::getNumPanels() const noexcept +{ + return holders.size(); +} + Component* ConcertinaPanel::getPanel (int index) const noexcept { if (PanelHolder* h = holders[index]) diff --git a/source/modules/juce_gui_basics/layout/juce_ConcertinaPanel.h b/source/modules/juce_gui_basics/layout/juce_ConcertinaPanel.h index b340a8bb8..2c25d2c98 100644 --- a/source/modules/juce_gui_basics/layout/juce_ConcertinaPanel.h +++ b/source/modules/juce_gui_basics/layout/juce_ConcertinaPanel.h @@ -85,7 +85,7 @@ public: expanded to that size. Otherwise, it'll fill as much of the total space as possible. */ - bool expandPanelFully (Component* panelComponent, const bool animate); + bool expandPanelFully (Component* panelComponent, bool animate); /** Sets a maximum size for one of the panels. */ void setMaximumPanelSize (Component* panelComponent, int maximumSize); @@ -100,7 +100,8 @@ public: virtual ~LookAndFeelMethods() {} virtual void drawConcertinaPanelHeader (Graphics&, const Rectangle& area, - bool isMouseOver, bool isMouseDown, ConcertinaPanel&, Component&) = 0; + bool isMouseOver, bool isMouseDown, + ConcertinaPanel&, Component&) = 0; }; private: diff --git a/source/modules/juce_gui_basics/layout/juce_Viewport.cpp b/source/modules/juce_gui_basics/layout/juce_Viewport.cpp index 282423387..80266cea6 100644 --- a/source/modules/juce_gui_basics/layout/juce_Viewport.cpp +++ b/source/modules/juce_gui_basics/layout/juce_Viewport.cpp @@ -52,6 +52,7 @@ Viewport::Viewport (const String& name) Viewport::~Viewport() { deleteContentComp(); + mouseWheelTimer = nullptr; } //============================================================================== @@ -357,6 +358,30 @@ static int rescaleMouseWheelDistance (float distance, int singleStepSize) noexce : jmax (distance, 1.0f)); } +// This puts a temporary component overlay over the content component, to prevent +// wheel events from reaching components inside it, so that while spinning a wheel +// with momentum, it won't accidentally scroll any subcomponents of the viewport. +struct Viewport::MouseWheelTimer : public Timer +{ + MouseWheelTimer (Viewport& v) : viewport (v) + { + viewport.contentHolder.addAndMakeVisible (dummyOverlay); + dummyOverlay.setAlwaysOnTop (true); + dummyOverlay.setPaintingIsUnclipped (true); + dummyOverlay.setBounds (viewport.contentHolder.getLocalBounds()); + } + + void timerCallback() override + { + viewport.mouseWheelTimer = nullptr; + } + + Component dummyOverlay; + Viewport& viewport; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MouseWheelTimer) +}; + bool Viewport::useMouseWheelMoveIfNeeded (const MouseEvent& e, const MouseWheelDetails& wheel) { if (! (e.mods.isAltDown() || e.mods.isCtrlDown() || e.mods.isCommandDown())) @@ -387,6 +412,11 @@ bool Viewport::useMouseWheelMoveIfNeeded (const MouseEvent& e, const MouseWheelD if (pos != getViewPosition()) { + if (mouseWheelTimer == nullptr) + mouseWheelTimer = new MouseWheelTimer (*this); + + mouseWheelTimer->startTimer (300); + setViewPosition (pos); return true; } diff --git a/source/modules/juce_gui_basics/layout/juce_Viewport.h b/source/modules/juce_gui_basics/layout/juce_Viewport.h index baeb42a72..c42751529 100644 --- a/source/modules/juce_gui_basics/layout/juce_Viewport.h +++ b/source/modules/juce_gui_basics/layout/juce_Viewport.h @@ -267,6 +267,9 @@ private: bool allowScrollingWithoutScrollbarV, allowScrollingWithoutScrollbarH; Component contentHolder; ScrollBar verticalScrollBar, horizontalScrollBar; + struct MouseWheelTimer; + ScopedPointer mouseWheelTimer; + Point viewportPosToCompPos (Point) const; void updateVisibleArea(); diff --git a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp index 4135afe3e..56aa11ae1 100644 --- a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp +++ b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp @@ -1397,7 +1397,6 @@ void LookAndFeel_V2::drawRotarySlider (Graphics& g, int x, int y, int width, int g.fillPath (filledArc); } - if (thickness > 0) { const float innerRadius = radius * 0.2f; Path p; @@ -1456,24 +1455,20 @@ Label* LookAndFeel_V2::createSliderTextBox (Slider& slider) Label* const l = new SliderLabelComp(); l->setJustificationType (Justification::centred); + l->setKeyboardType (TextInputTarget::decimalKeyboard); l->setColour (Label::textColourId, slider.findColour (Slider::textBoxTextColourId)); - l->setColour (Label::backgroundColourId, (slider.getSliderStyle() == Slider::LinearBar || slider.getSliderStyle() == Slider::LinearBarVertical) ? Colours::transparentBlack : slider.findColour (Slider::textBoxBackgroundColourId)); l->setColour (Label::outlineColourId, slider.findColour (Slider::textBoxOutlineColourId)); - l->setColour (TextEditor::textColourId, slider.findColour (Slider::textBoxTextColourId)); - l->setColour (TextEditor::backgroundColourId, slider.findColour (Slider::textBoxBackgroundColourId) .withAlpha ((slider.getSliderStyle() == Slider::LinearBar || slider.getSliderStyle() == Slider::LinearBarVertical) ? 0.7f : 1.0f)); - l->setColour (TextEditor::outlineColourId, slider.findColour (Slider::textBoxOutlineColourId)); - l->setColour (TextEditor::highlightColourId, slider.findColour (Slider::textBoxHighlightColourId)); return l; @@ -2509,7 +2504,7 @@ void LookAndFeel_V2::layoutFileBrowserComponent (FileBrowserComponent& browserCo filenameBox->setBounds (x + 50, y, w - 50, controlsHeight); } -// Pulls a drawable out of compressed valuetree data.. +// Pulls a drawable out of compressed ValueTree data.. static Drawable* loadDrawableFromData (const void* data, size_t numBytes) { MemoryInputStream m (data, numBytes, false); diff --git a/source/modules/juce_gui_basics/menus/juce_PopupMenu.cpp b/source/modules/juce_gui_basics/menus/juce_PopupMenu.cpp index b2e64a315..8dcd514e5 100644 --- a/source/modules/juce_gui_basics/menus/juce_PopupMenu.cpp +++ b/source/modules/juce_gui_basics/menus/juce_PopupMenu.cpp @@ -1543,6 +1543,7 @@ int PopupMenu::showWithOptionalCallback (const Options& options, ModalComponentM if (userCallback == nullptr && canBeModal) return window->runModalLoop(); #else + (void) canBeModal; jassert (! (userCallback == nullptr && canBeModal)); #endif } diff --git a/source/modules/juce_gui_basics/mouse/juce_SelectedItemSet.h b/source/modules/juce_gui_basics/mouse/juce_SelectedItemSet.h index 20f1f7b99..2566c433c 100644 --- a/source/modules/juce_gui_basics/mouse/juce_SelectedItemSet.h +++ b/source/modules/juce_gui_basics/mouse/juce_SelectedItemSet.h @@ -38,8 +38,6 @@ To be informed when items are selected/deselected, register a ChangeListener with this object. - - @see SelectableObject */ template class SelectedItemSet : public ChangeBroadcaster diff --git a/source/modules/juce_gui_basics/native/juce_android_Windowing.cpp b/source/modules/juce_gui_basics/native/juce_android_Windowing.cpp index 200615f23..6946c68ea 100644 --- a/source/modules/juce_gui_basics/native/juce_android_Windowing.cpp +++ b/source/modules/juce_gui_basics/native/juce_android_Windowing.cpp @@ -111,7 +111,8 @@ public: // NB: must not put this in the initialiser list, as it invokes a callback, // which will fail if the peer is only half-constructed. view = GlobalRef (android.activity.callObjectMethod (JuceAppActivity.createNewView, - component.isOpaque(), (jlong) this)); + (jboolean) component.isOpaque(), + (jlong) this)); if (isFocused()) handleFocusGain(); @@ -384,6 +385,7 @@ public: { case TextInputTarget::textKeyboard: return "text"; case TextInputTarget::numericKeyboard: return "number"; + case TextInputTarget::decimalKeyboard: return "numberDecimal"; case TextInputTarget::urlKeyboard: return "textUri"; case TextInputTarget::emailAddressKeyboard: return "textEmailAddress"; case TextInputTarget::phoneNumberKeyboard: return "phone"; @@ -584,7 +586,7 @@ ComponentPeer* Component::createNewPeer (int styleFlags, void*) jobject createOpenGLView (ComponentPeer* peer) { - jobject parentView = static_cast (peer->getNativeHandle()); + jobject parentView = static_cast (peer->getNativeHandle()); return getEnv()->CallObjectMethod (parentView, ComponentPeerView.createGLView); } diff --git a/source/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm b/source/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm index b139ccffb..f35b1c83b 100644 --- a/source/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm +++ b/source/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm @@ -862,12 +862,18 @@ void UIViewComponentPeer::textInputRequired (Point, TextInputTarget&) { } +static bool isIOS4_1() noexcept +{ + return [[[UIDevice currentDevice] systemVersion] doubleValue] >= 4.1; +} + static UIKeyboardType getUIKeyboardType (TextInputTarget::VirtualKeyboardType type) noexcept { switch (type) { case TextInputTarget::textKeyboard: return UIKeyboardTypeAlphabet; - case TextInputTarget::numericKeyboard: return UIKeyboardTypeNumbersAndPunctuation; + case TextInputTarget::numericKeyboard: return isIOS4_1() ? UIKeyboardTypeNumberPad : UIKeyboardTypeNumbersAndPunctuation; + case TextInputTarget::decimalKeyboard: return isIOS4_1() ? UIKeyboardTypeDecimalPad : UIKeyboardTypeNumbersAndPunctuation; case TextInputTarget::urlKeyboard: return UIKeyboardTypeURL; case TextInputTarget::emailAddressKeyboard: return UIKeyboardTypeEmailAddress; case TextInputTarget::phoneNumberKeyboard: return UIKeyboardTypePhonePad; @@ -881,7 +887,7 @@ void UIViewComponentPeer::updateHiddenTextContent (TextInputTarget* target) { view->hiddenTextView.keyboardType = getUIKeyboardType (target->getKeyboardType()); view->hiddenTextView.text = juceStringToNS (target->getTextInRange (Range (0, target->getHighlightedRegion().getStart()))); - view->hiddenTextView.selectedRange = NSMakeRange (target->getHighlightedRegion().getStart(), 0); + view->hiddenTextView.selectedRange = NSMakeRange ((NSUInteger) target->getHighlightedRegion().getStart(), 0); } BOOL UIViewComponentPeer::textViewReplaceCharacters (Range range, const String& text) diff --git a/source/modules/juce_gui_basics/native/juce_linux_Clipboard.cpp b/source/modules/juce_gui_basics/native/juce_linux_Clipboard.cpp index b64c85c25..3b77c5349 100644 --- a/source/modules/juce_gui_basics/native/juce_linux_Clipboard.cpp +++ b/source/modules/juce_gui_basics/native/juce_linux_Clipboard.cpp @@ -36,8 +36,11 @@ namespace ClipboardHelpers static void initSelectionAtoms() { static bool isInitialised = false; + if (! isInitialised) { + isInitialised = true; + atom_UTF8_STRING = XInternAtom (display, "UTF8_STRING", False); atom_CLIPBOARD = XInternAtom (display, "CLIPBOARD", False); atom_TARGETS = XInternAtom (display, "TARGETS", False); @@ -50,29 +53,34 @@ namespace ClipboardHelpers static String readWindowProperty (Window window, Atom prop) { String returnData; - char* clipData; - Atom actualType; - int actualFormat; - unsigned long numItems, bytesLeft; - - if (XGetWindowProperty (display, window, prop, - 0L /* offset */, 1000000 /* length (max) */, False, - AnyPropertyType /* format */, - &actualType, &actualFormat, &numItems, &bytesLeft, - (unsigned char**) &clipData) == Success) + + if (display != nullptr) { - if (actualType == atom_UTF8_STRING && actualFormat == 8) - returnData = String::fromUTF8 (clipData, numItems); - else if (actualType == XA_STRING && actualFormat == 8) - returnData = String (clipData, numItems); + char* clipData; + Atom actualType; + int actualFormat; + unsigned long numItems, bytesLeft; + + if (XGetWindowProperty (display, window, prop, + 0L /* offset */, 1000000 /* length (max) */, False, + AnyPropertyType /* format */, + &actualType, &actualFormat, &numItems, &bytesLeft, + (unsigned char**) &clipData) == Success) + { + if (actualType == atom_UTF8_STRING && actualFormat == 8) + returnData = String::fromUTF8 (clipData, numItems); + else if (actualType == XA_STRING && actualFormat == 8) + returnData = String (clipData, numItems); + + if (clipData != nullptr) + XFree (clipData); - if (clipData != nullptr) - XFree (clipData); + jassert (bytesLeft == 0 || numItems == 1000000); + } - jassert (bytesLeft == 0 || numItems == 1000000); + XDeleteProperty (display, window, prop); } - XDeleteProperty (display, window, prop); return returnData; } @@ -92,6 +100,7 @@ namespace ClipboardHelpers while (--count >= 0) { XEvent event; + if (XCheckTypedWindowEvent (display, juce_messageWindowHandle, SelectionNotify, &event)) { if (event.xselection.property == property_name) @@ -102,10 +111,8 @@ namespace ClipboardHelpers event.xselection.property); return true; } - else - { - return false; // the format we asked for was denied.. (event.xselection.property == None) - } + + return false; // the format we asked for was denied.. (event.xselection.property == None) } // not very elegant.. we could do a select() or something like that... @@ -121,66 +128,69 @@ namespace ClipboardHelpers // Called from the event loop in juce_linux_Messaging in response to SelectionRequest events static void handleSelection (XSelectionRequestEvent& evt) { - ClipboardHelpers::initSelectionAtoms(); - - // the selection content is sent to the target window as a window property - XSelectionEvent reply; - reply.type = SelectionNotify; - reply.display = evt.display; - reply.requestor = evt.requestor; - reply.selection = evt.selection; - reply.target = evt.target; - reply.property = None; // == "fail" - reply.time = evt.time; - - HeapBlock data; - int propertyFormat = 0; - size_t numDataItems = 0; - - if (evt.selection == XA_PRIMARY || evt.selection == ClipboardHelpers::atom_CLIPBOARD) + if (display != nullptr) { - if (evt.target == XA_STRING || evt.target == ClipboardHelpers::atom_UTF8_STRING) + ClipboardHelpers::initSelectionAtoms(); + + // the selection content is sent to the target window as a window property + XSelectionEvent reply; + reply.type = SelectionNotify; + reply.display = evt.display; + reply.requestor = evt.requestor; + reply.selection = evt.selection; + reply.target = evt.target; + reply.property = None; // == "fail" + reply.time = evt.time; + + HeapBlock data; + int propertyFormat = 0; + size_t numDataItems = 0; + + if (evt.selection == XA_PRIMARY || evt.selection == ClipboardHelpers::atom_CLIPBOARD) { - // translate to utf8 - numDataItems = ClipboardHelpers::localClipboardContent.getNumBytesAsUTF8() + 1; - data.calloc (numDataItems + 1); - ClipboardHelpers::localClipboardContent.copyToUTF8 (data, numDataItems); - propertyFormat = 8; // bits/item + if (evt.target == XA_STRING || evt.target == ClipboardHelpers::atom_UTF8_STRING) + { + // translate to utf8 + numDataItems = ClipboardHelpers::localClipboardContent.getNumBytesAsUTF8() + 1; + data.calloc (numDataItems + 1); + ClipboardHelpers::localClipboardContent.copyToUTF8 (data, numDataItems); + propertyFormat = 8; // bits/item + } + else if (evt.target == ClipboardHelpers::atom_TARGETS) + { + // another application wants to know what we are able to send + numDataItems = 2; + propertyFormat = 32; // atoms are 32-bit + data.calloc (numDataItems * 4); + Atom* atoms = reinterpret_cast (data.getData()); + atoms[0] = ClipboardHelpers::atom_UTF8_STRING; + atoms[1] = XA_STRING; + + evt.target = XA_ATOM; + } } - else if (evt.target == ClipboardHelpers::atom_TARGETS) + else { - // another application wants to know what we are able to send - numDataItems = 2; - propertyFormat = 32; // atoms are 32-bit - data.calloc (numDataItems * 4); - Atom* atoms = reinterpret_cast (data.getData()); - atoms[0] = ClipboardHelpers::atom_UTF8_STRING; - atoms[1] = XA_STRING; - - evt.target = XA_ATOM; + DBG ("requested unsupported clipboard"); } - } - else - { - DBG ("requested unsupported clipboard"); - } - if (data != nullptr) - { - const size_t maxReasonableSelectionSize = 1000000; - - // for very big chunks of data, we should use the "INCR" protocol , which is a pain in the *ss - if (evt.property != None && numDataItems < maxReasonableSelectionSize) + if (data != nullptr) { - XChangeProperty (evt.display, evt.requestor, - evt.property, evt.target, - propertyFormat /* 8 or 32 */, PropModeReplace, - reinterpret_cast (data.getData()), numDataItems); - reply.property = evt.property; // " == success" + const size_t maxReasonableSelectionSize = 1000000; + + // for very big chunks of data, we should use the "INCR" protocol , which is a pain in the *ss + if (evt.property != None && numDataItems < maxReasonableSelectionSize) + { + XChangeProperty (evt.display, evt.requestor, + evt.property, evt.target, + propertyFormat /* 8 or 32 */, PropModeReplace, + reinterpret_cast (data.getData()), numDataItems); + reply.property = evt.property; // " == success" + } } - } - XSendEvent (evt.display, evt.requestor, 0, NoEventMask, (XEvent*) &reply); + XSendEvent (evt.display, evt.requestor, 0, NoEventMask, (XEvent*) &reply); + } } } @@ -201,51 +211,58 @@ static ClipboardCallbackInitialiser clipboardInitialiser; //============================================================================== void SystemClipboard::copyTextToClipboard (const String& clipText) { - ClipboardHelpers::initSelectionAtoms(); - ClipboardHelpers::localClipboardContent = clipText; + if (display != nullptr) + { + ClipboardHelpers::initSelectionAtoms(); + ClipboardHelpers::localClipboardContent = clipText; - XSetSelectionOwner (display, XA_PRIMARY, juce_messageWindowHandle, CurrentTime); - XSetSelectionOwner (display, ClipboardHelpers::atom_CLIPBOARD, juce_messageWindowHandle, CurrentTime); + XSetSelectionOwner (display, XA_PRIMARY, juce_messageWindowHandle, CurrentTime); + XSetSelectionOwner (display, ClipboardHelpers::atom_CLIPBOARD, juce_messageWindowHandle, CurrentTime); + } } String SystemClipboard::getTextFromClipboard() { - ClipboardHelpers::initSelectionAtoms(); - - /* 1) try to read from the "CLIPBOARD" selection first (the "high - level" clipboard that is supposed to be filled by ctrl-C - etc). When a clipboard manager is running, the content of this - selection is preserved even when the original selection owner - exits. - - 2) and then try to read from "PRIMARY" selection (the "legacy" selection - filled by good old x11 apps such as xterm) - */ String content; - Atom selection = XA_PRIMARY; - Window selectionOwner = None; - if ((selectionOwner = XGetSelectionOwner (display, selection)) == None) + if (display != nullptr) { - selection = ClipboardHelpers::atom_CLIPBOARD; - selectionOwner = XGetSelectionOwner (display, selection); - } + ClipboardHelpers::initSelectionAtoms(); - if (selectionOwner != None) - { - if (selectionOwner == juce_messageWindowHandle) + /* 1) try to read from the "CLIPBOARD" selection first (the "high + level" clipboard that is supposed to be filled by ctrl-C + etc). When a clipboard manager is running, the content of this + selection is preserved even when the original selection owner + exits. + + 2) and then try to read from "PRIMARY" selection (the "legacy" selection + filled by good old x11 apps such as xterm) + */ + Atom selection = XA_PRIMARY; + Window selectionOwner = None; + + if ((selectionOwner = XGetSelectionOwner (display, selection)) == None) { - content = ClipboardHelpers::localClipboardContent; + selection = ClipboardHelpers::atom_CLIPBOARD; + selectionOwner = XGetSelectionOwner (display, selection); } - else - { - // first try: we want an utf8 string - bool ok = ClipboardHelpers::requestSelectionContent (content, selection, ClipboardHelpers::atom_UTF8_STRING); - if (! ok) + if (selectionOwner != None) + { + if (selectionOwner == juce_messageWindowHandle) { - // second chance, ask for a good old locale-dependent string .. - ok = ClipboardHelpers::requestSelectionContent (content, selection, XA_STRING); + content = ClipboardHelpers::localClipboardContent; + } + else + { + // first try: we want an utf8 string + bool ok = ClipboardHelpers::requestSelectionContent (content, selection, ClipboardHelpers::atom_UTF8_STRING); + + if (! ok) + { + // second chance, ask for a good old locale-dependent string .. + ok = ClipboardHelpers::requestSelectionContent (content, selection, XA_STRING); + } } } } diff --git a/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp b/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp index 8ae63987c..79152b356 100644 --- a/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp +++ b/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp @@ -165,6 +165,9 @@ namespace Keys bool KeyPress::isKeyCurrentlyDown (const int keyCode) { + if (display == nullptr) + return false; + int keysym; if (keyCode & Keys::extendedKeyModifier) @@ -212,54 +215,58 @@ namespace XSHMHelpers if (! isChecked) { isChecked = true; - int major, minor; - Bool pixmaps; - ScopedXLock xlock; - - if (XShmQueryVersion (display, &major, &minor, &pixmaps)) + if (display != nullptr) { - trappedErrorCode = 0; - XErrorHandler oldHandler = XSetErrorHandler (errorTrapHandler); - - XShmSegmentInfo segmentInfo; - zerostruct (segmentInfo); + int major, minor; + Bool pixmaps; - XImage* xImage = XShmCreateImage (display, DefaultVisual (display, DefaultScreen (display)), - 24, ZPixmap, 0, &segmentInfo, 50, 50); + ScopedXLock xlock; - if ((segmentInfo.shmid = shmget (IPC_PRIVATE, - xImage->bytes_per_line * xImage->height, - IPC_CREAT | 0777)) >= 0) + if (XShmQueryVersion (display, &major, &minor, &pixmaps)) { - segmentInfo.shmaddr = (char*) shmat (segmentInfo.shmid, 0, 0); + trappedErrorCode = 0; + XErrorHandler oldHandler = XSetErrorHandler (errorTrapHandler); - if (segmentInfo.shmaddr != (void*) -1) + XShmSegmentInfo segmentInfo; + zerostruct (segmentInfo); + + XImage* xImage = XShmCreateImage (display, DefaultVisual (display, DefaultScreen (display)), + 24, ZPixmap, 0, &segmentInfo, 50, 50); + + if ((segmentInfo.shmid = shmget (IPC_PRIVATE, + xImage->bytes_per_line * xImage->height, + IPC_CREAT | 0777)) >= 0) { - segmentInfo.readOnly = False; - xImage->data = segmentInfo.shmaddr; - XSync (display, False); + segmentInfo.shmaddr = (char*) shmat (segmentInfo.shmid, 0, 0); - if (XShmAttach (display, &segmentInfo) != 0) + if (segmentInfo.shmaddr != (void*) -1) { + segmentInfo.readOnly = False; + xImage->data = segmentInfo.shmaddr; XSync (display, False); - XShmDetach (display, &segmentInfo); - isAvailable = true; + if (XShmAttach (display, &segmentInfo) != 0) + { + XSync (display, False); + XShmDetach (display, &segmentInfo); + + isAvailable = true; + } } - } - XFlush (display); - XDestroyImage (xImage); + XFlush (display); + XDestroyImage (xImage); - shmdt (segmentInfo.shmaddr); - } + shmdt (segmentInfo.shmaddr); + } - shmctl (segmentInfo.shmid, IPC_RMID, 0); + shmctl (segmentInfo.shmid, IPC_RMID, 0); - XSetErrorHandler (oldHandler); - if (trappedErrorCode != 0) - isAvailable = false; + XSetErrorHandler (oldHandler); + if (trappedErrorCode != 0) + isAvailable = false; + } } } @@ -288,25 +295,29 @@ namespace XRender if (! hasLoaded) { - ScopedXLock xlock; - hasLoaded = true; - - if (void* h = dlopen ("libXrender.so", RTLD_GLOBAL | RTLD_NOW)) + if (display != nullptr) { - xRenderQueryVersion = (tXRenderQueryVersion) dlsym (h, "XRenderQueryVersion"); - xRenderFindStandardFormat = (tXRenderFindStandardFormat) dlsym (h, "XRenderFindStandardFormat"); - xRenderFindFormat = (tXRenderFindFormat) dlsym (h, "XRenderFindFormat"); - xRenderFindVisualFormat = (tXRenderFindVisualFormat) dlsym (h, "XRenderFindVisualFormat"); - } + hasLoaded = true; - if (xRenderQueryVersion != nullptr - && xRenderFindStandardFormat != nullptr - && xRenderFindFormat != nullptr - && xRenderFindVisualFormat != nullptr) - { - int major, minor; - if (xRenderQueryVersion (display, &major, &minor)) - return true; + ScopedXLock xlock; + + if (void* h = dlopen ("libXrender.so", RTLD_GLOBAL | RTLD_NOW)) + { + xRenderQueryVersion = (tXRenderQueryVersion) dlsym (h, "XRenderQueryVersion"); + xRenderFindStandardFormat = (tXRenderFindStandardFormat) dlsym (h, "XRenderFindStandardFormat"); + xRenderFindFormat = (tXRenderFindFormat) dlsym (h, "XRenderFindFormat"); + xRenderFindVisualFormat = (tXRenderFindVisualFormat) dlsym (h, "XRenderFindVisualFormat"); + } + + if (xRenderQueryVersion != nullptr + && xRenderFindStandardFormat != nullptr + && xRenderFindFormat != nullptr + && xRenderFindVisualFormat != nullptr) + { + int major, minor; + if (xRenderQueryVersion (display, &major, &minor)) + return true; + } } xRenderQueryVersion = nullptr; @@ -317,7 +328,7 @@ namespace XRender static bool hasCompositingWindowManager() { - return XGetSelectionOwner (display, Atoms::get().compositingManager) != 0; + return display != nullptr && XGetSelectionOwner (display, Atoms::get().compositingManager) != 0; } static XRenderPictFormat* findPictureFormat() @@ -689,9 +700,9 @@ public: p += srcData.pixelStride; XPutPixel (xImage, x, y, - (((((uint32) pixel->getRed()) << rShiftL) >> rShiftR) & rMask) + (((((uint32) pixel->getRed()) << rShiftL) >> rShiftR) & rMask) | (((((uint32) pixel->getGreen()) << gShiftL) >> gShiftR) & gMask) - | (((((uint32) pixel->getBlue()) << bShiftL) >> bShiftR) & bMask)); + | (((((uint32) pixel->getBlue()) << bShiftL) >> bShiftR) & bMask)); } } } @@ -709,8 +720,8 @@ private: //============================================================================== XImage* xImage; const int imageDepth; - HeapBlock imageDataAllocated; - HeapBlock imageData16Bit; + HeapBlock imageDataAllocated; + HeapBlock imageData16Bit; int pixelStride, lineStride; uint8* imageData; GC gc; @@ -742,7 +753,7 @@ namespace PixmapHelpers const int width = image.getWidth(); const int height = image.getHeight(); - HeapBlock colour (width * height); + HeapBlock colour (width * height); int index = 0; for (int y = 0; y < height; ++y) @@ -770,7 +781,7 @@ namespace PixmapHelpers const int width = image.getWidth(); const int height = image.getHeight(); const int stride = (width + 7) >> 3; - HeapBlock mask; + HeapBlock mask; mask.calloc (stride * height); const bool msbfirst = (BitmapBitOrder (display) == MSBFirst); @@ -872,17 +883,22 @@ public: { XPointer peer = nullptr; - ScopedXLock xlock; - if (! XFindContext (display, (XID) windowHandle, windowHandleXContext, &peer)) - if (peer != nullptr && ! ComponentPeer::isValidPeer (reinterpret_cast (peer))) - peer = nullptr; + if (display != nullptr) + { + ScopedXLock xlock; - return reinterpret_cast (peer); + if (! XFindContext (display, (XID) windowHandle, windowHandleXContext, &peer)) + if (peer != nullptr && ! ComponentPeer::isValidPeer (reinterpret_cast (peer))) + peer = nullptr; + } + + return reinterpret_cast (peer); } void setVisible (bool shouldBeVisible) override { ScopedXLock xlock; + if (shouldBeVisible) XMapWindow (display, windowH); else @@ -892,7 +908,7 @@ public: void setTitle (const String& title) override { XTextProperty nameProperty; - char* strings[] = { const_cast (title.toRawUTF8()) }; + char* strings[] = { const_cast (title.toRawUTF8()) }; ScopedXLock xlock; if (XStringListToTextProperty (strings, 1, &nameProperty)) @@ -979,13 +995,11 @@ public: Point localToGlobal (Point relativePosition) override { - updateWindowBounds(); return relativePosition + bounds.getPosition().toFloat(); } Point globalToLocal (Point screenPosition) override { - updateWindowBounds(); return screenPosition - bounds.getPosition().toFloat(); } @@ -1237,7 +1251,7 @@ public: void setIcon (const Image& newIcon) override { const int dataSize = newIcon.getWidth() * newIcon.getHeight() + 2; - HeapBlock data (dataSize); + HeapBlock data (dataSize); int index = 0; data[index++] = (unsigned long) newIcon.getWidth(); @@ -1374,7 +1388,7 @@ public: const ModifierKeys oldMods (currentModifiers); bool keyPressed = false; - if ((sym & 0xff00) == 0xff00 || sym == XK_ISO_Left_Tab) + if ((sym & 0xff00) == 0xff00 || keyCode == XK_ISO_Left_Tab) { switch (sym) // Translate keypad { @@ -1433,6 +1447,11 @@ public: keyCode &= 0xff; break; + case XK_ISO_Left_Tab: + keyPressed = true; + keyCode = XK_Tab & 0xff; + break; + default: if (sym >= XK_F1 && sym <= XK_F16) { @@ -1942,7 +1961,7 @@ private: JUCE_DECLARE_NON_COPYABLE (LinuxRepaintManager) }; - ScopedPointer repainter; + ScopedPointer repainter; friend class LinuxRepaintManager; Window windowH, parentWindow; @@ -2701,7 +2720,7 @@ private: srcMimeTypeAtomList.clear(); dragAndDropCurrentMimeType = 0; - const unsigned long dndCurrentVersion = static_cast (clientMsg.data.l[1] & 0xff000000) >> 24; + const unsigned long dndCurrentVersion = static_cast (clientMsg.data.l[1] & 0xff000000) >> 24; if (dndCurrentVersion < 3 || dndCurrentVersion > Atoms::DndVersion) { @@ -2898,7 +2917,7 @@ private: Window dragAndDropSourceWindow; bool finishAfterDropDataReceived; - Array srcMimeTypeAtomList; + Array srcMimeTypeAtomList; int pointerMap[5]; @@ -2958,22 +2977,26 @@ void ModifierKeys::updateCurrentModifiers() noexcept ModifierKeys ModifierKeys::getCurrentModifiersRealtime() noexcept { - Window root, child; - int x, y, winx, winy; - unsigned int mask; - int mouseMods = 0; + if (display != nullptr) + { + Window root, child; + int x, y, winx, winy; + unsigned int mask; + int mouseMods = 0; - ScopedXLock xlock; + ScopedXLock xlock; - if (XQueryPointer (display, RootWindow (display, DefaultScreen (display)), - &root, &child, &x, &y, &winx, &winy, &mask) != False) - { - if ((mask & Button1Mask) != 0) mouseMods |= ModifierKeys::leftButtonModifier; - if ((mask & Button2Mask) != 0) mouseMods |= ModifierKeys::middleButtonModifier; - if ((mask & Button3Mask) != 0) mouseMods |= ModifierKeys::rightButtonModifier; + if (XQueryPointer (display, RootWindow (display, DefaultScreen (display)), + &root, &child, &x, &y, &winx, &winy, &mask) != False) + { + if ((mask & Button1Mask) != 0) mouseMods |= ModifierKeys::leftButtonModifier; + if ((mask & Button2Mask) != 0) mouseMods |= ModifierKeys::middleButtonModifier; + if ((mask & Button3Mask) != 0) mouseMods |= ModifierKeys::rightButtonModifier; + } + + LinuxComponentPeer::currentModifiers = LinuxComponentPeer::currentModifiers.withoutMouseButtons().withFlags (mouseMods); } - LinuxComponentPeer::currentModifiers = LinuxComponentPeer::currentModifiers.withoutMouseButtons().withFlags (mouseMods); return LinuxComponentPeer::currentModifiers; } @@ -3136,6 +3159,9 @@ bool Desktop::canUseSemiTransparentWindows() noexcept Point MouseInputSource::getCurrentRawMousePosition() { + if (display == nullptr) + return Point(); + Window root, child; int x, y, winx, winy; unsigned int mask; @@ -3156,9 +3182,12 @@ Point MouseInputSource::getCurrentRawMousePosition() void MouseInputSource::setRawMousePosition (Point newPosition) { - ScopedXLock xlock; - Window root = RootWindow (display, DefaultScreen (display)); - XWarpPointer (display, None, root, 0, 0, 0, 0, roundToInt (newPosition.getX()), roundToInt (newPosition.getY())); + if (display != nullptr) + { + ScopedXLock xlock; + Window root = RootWindow (display, DefaultScreen (display)); + XWarpPointer (display, None, root, 0, 0, 0, 0, roundToInt (newPosition.getX()), roundToInt (newPosition.getY())); + } } double Desktop::getDefaultMasterScale() @@ -3180,16 +3209,19 @@ void Desktop::setScreenSaverEnabled (const bool isEnabled) { screenSaverAllowed = isEnabled; - typedef void (*tXScreenSaverSuspend) (Display*, Bool); - static tXScreenSaverSuspend xScreenSaverSuspend = nullptr; + if (display != nullptr) + { + typedef void (*tXScreenSaverSuspend) (Display*, Bool); + static tXScreenSaverSuspend xScreenSaverSuspend = nullptr; - if (xScreenSaverSuspend == nullptr) - if (void* h = dlopen ("libXss.so", RTLD_GLOBAL | RTLD_NOW)) - xScreenSaverSuspend = (tXScreenSaverSuspend) dlsym (h, "XScreenSaverSuspend"); + if (xScreenSaverSuspend == nullptr) + if (void* h = dlopen ("libXss.so", RTLD_GLOBAL | RTLD_NOW)) + xScreenSaverSuspend = (tXScreenSaverSuspend) dlsym (h, "XScreenSaverSuspend"); - ScopedXLock xlock; - if (xScreenSaverSuspend != nullptr) - xScreenSaverSuspend (display, ! isEnabled); + ScopedXLock xlock; + if (xScreenSaverSuspend != nullptr) + xScreenSaverSuspend (display, ! isEnabled); + } } } @@ -3201,6 +3233,9 @@ bool Desktop::isScreenSaverEnabled() //============================================================================== void* CustomMouseCursorInfo::create() const { + if (display == nullptr) + return nullptr; + ScopedXLock xlock; const unsigned int imageW = image.getWidth(); const unsigned int imageH = image.getHeight(); @@ -3286,7 +3321,7 @@ void* CustomMouseCursorInfo::create() const } const int stride = (cursorW + 7) >> 3; - HeapBlock maskPlane, sourcePlane; + HeapBlock maskPlane, sourcePlane; maskPlane.calloc (stride * cursorH); sourcePlane.calloc (stride * cursorH); @@ -3323,13 +3358,18 @@ void* CustomMouseCursorInfo::create() const void MouseCursor::deleteMouseCursor (void* const cursorHandle, const bool) { - ScopedXLock xlock; - if (cursorHandle != nullptr) + if (cursorHandle != nullptr && display != nullptr) + { + ScopedXLock xlock; XFreeCursor (display, (Cursor) cursorHandle); + } } void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType type) { + if (display == nullptr) + return None; + unsigned int shape; switch (type) diff --git a/source/modules/juce_gui_basics/native/juce_mac_FileChooser.mm b/source/modules/juce_gui_basics/native/juce_mac_FileChooser.mm index d5f36a6f2..4691a0c4f 100644 --- a/source/modules/juce_gui_basics/native/juce_mac_FileChooser.mm +++ b/source/modules/juce_gui_basics/native/juce_mac_FileChooser.mm @@ -29,9 +29,11 @@ struct FileChooserDelegateClass : public ObjCClass FileChooserDelegateClass() : ObjCClass ("JUCEFileChooser_") { addIvar ("filters"); + addIvar ("filePreviewComponent"); - addMethod (@selector (dealloc), dealloc, "v@:"); - addMethod (@selector (panel:shouldShowFilename:), shouldShowFilename, "c@:@@"); + addMethod (@selector (dealloc), dealloc, "v@:"); + addMethod (@selector (panel:shouldShowFilename:), shouldShowFilename, "c@:@@"); + addMethod (@selector (panelSelectionDidChange:), panelSelectionDidChange, "c@"); #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 addProtocol (@protocol (NSOpenSavePanelDelegate)); @@ -40,21 +42,21 @@ struct FileChooserDelegateClass : public ObjCClass registerClass(); } - static void setFilters (id self, StringArray* filters) - { - object_setInstanceVariable (self, "filters", filters); - } + static void setFilters (id self, StringArray* filters) { object_setInstanceVariable (self, "filters", filters); } + static void setFilePreviewComponent (id self, FilePreviewComponent* comp) { object_setInstanceVariable (self, "filePreviewComponent", comp); } + static StringArray* getFilters (id self) { return getIvar (self, "filters"); } + static FilePreviewComponent* getFilePreviewComponent (id self) { return getIvar (self, "filePreviewComponent"); } private: static void dealloc (id self, SEL) { - delete getIvar (self, "filters"); + delete getFilters (self); sendSuperclassMessage (self, @selector (dealloc)); } static BOOL shouldShowFilename (id self, SEL, id /*sender*/, NSString* filename) { - StringArray* const filters = getIvar (self, "filters"); + StringArray* const filters = getFilters (self); const File f (nsStringToJuce (filename)); @@ -81,6 +83,32 @@ private: return f.isDirectory() && ! [[NSWorkspace sharedWorkspace] isFilePackageAtPath: filename]; } + + static StringArray getSelectedPaths (id sender) + { + StringArray paths; + + if ([sender isKindOfClass: [NSOpenPanel class]]) + { + NSArray* urls = [(NSOpenPanel*) sender URLs]; + + for (NSUInteger i = 0; i < [urls count]; ++i) + paths.add (nsStringToJuce ([[urls objectAtIndex: i] path])); + } + else if ([sender isKindOfClass: [NSSavePanel class]]) + { + paths.add (nsStringToJuce ([[(NSSavePanel*) sender URL] path])); + } + + return paths; + } + + static void panelSelectionDidChange (id self, SEL, id sender) + { + // NB: would need to extend FilePreviewComponent to handle the full list rather than just the first one + if (FilePreviewComponent* const previewComp = getFilePreviewComponent (self)) + previewComp->selectedFileChanged (File (getSelectedPaths (sender)[0])); + } }; static NSMutableArray* createAllowedTypesArray (const StringArray& filters) @@ -113,7 +141,7 @@ void FileChooser::showPlatformDialog (Array& results, bool isSaveDialogue, bool /*warnAboutOverwritingExistingFiles*/, bool selectMultipleFiles, - FilePreviewComponent* /*extraInfoComponent*/) + FilePreviewComponent* extraInfoComponent) { JUCE_AUTORELEASEPOOL { @@ -151,6 +179,16 @@ void FileChooser::showPlatformDialog (Array& results, [openPanel setResolvesAliases: YES]; } + if (extraInfoComponent != nullptr) + { + NSView* view = [[[NSView alloc] initWithFrame: makeNSRect (extraInfoComponent->getLocalBounds())] autorelease]; + extraInfoComponent->addToDesktop (0, (void*) view); + extraInfoComponent->setVisible (true); + FileChooserDelegateClass::setFilePreviewComponent (delegate, extraInfoComponent); + + [panel setAccessoryView: view]; + } + [panel setDelegate: delegate]; if (isSaveDialogue || selectsDirectory) @@ -172,10 +210,10 @@ void FileChooser::showPlatformDialog (Array& results, [panel setDirectoryURL: [NSURL fileURLWithPath: juceStringToNS (directory)]]; [panel setNameFieldStringValue: juceStringToNS (filename)]; - if ([panel runModal] == NSOKButton) + if ([panel runModal] == 1 /*NSModalResponseOK*/) #else if ([panel runModalForDirectory: juceStringToNS (directory) - file: juceStringToNS (filename)] == NSOKButton) + file: juceStringToNS (filename)] == 1 /*NSModalResponseOK*/) #endif { if (isSaveDialogue) diff --git a/source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm b/source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm index 45569b83b..8bd4d5850 100644 --- a/source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm +++ b/source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm @@ -411,9 +411,9 @@ public: if (NSWindow* const viewWindow = [view window]) { const NSRect windowFrame = [viewWindow frame]; - const NSPoint windowPoint = [view convertPoint: NSMakePoint (localPos.x, localPos.y) toView: nil]; + const NSPoint windowPoint = [view convertPoint: NSMakePoint (localPos.x, viewFrame.size.height - localPos.y) toView: nil]; const NSPoint screenPoint = NSMakePoint (windowFrame.origin.x + windowPoint.x, - windowFrame.origin.y + windowFrame.size.height - windowPoint.y); + windowFrame.origin.y + windowPoint.y); if (! isWindowAtPoint (viewWindow, screenPoint)) return false; @@ -574,7 +574,15 @@ public: { currentModifiers = currentModifiers.withoutMouseButtons(); - if (isWindowAtPoint ([ev window], [[ev window] convertBaseToScreen: [ev locationInWindow]])) + NSPoint windowPos = [ev locationInWindow]; + + #if defined (MAC_OS_X_VERSION_10_7) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7 + NSPoint screenPos = [[ev window] convertRectToScreen: NSMakeRect (windowPos.x, windowPos.y, 1.0f, 1.0f)].origin; + #else + NSPoint screenPos = [[ev window] convertBaseToScreen: windowPos]; + #endif + + if (isWindowAtPoint ([ev window], screenPos)) sendMouseEvent (ev); else // moved into another window which overlaps this one, so trigger an exit @@ -634,8 +642,8 @@ public: #endif if ([ev respondsToSelector: @selector (deviceDeltaX)]) { - wheel.deltaX = checkDeviceDeltaReturnValue ((float) objc_msgSend_fpret (ev, @selector (deviceDeltaX))); - wheel.deltaY = checkDeviceDeltaReturnValue ((float) objc_msgSend_fpret (ev, @selector (deviceDeltaY))); + wheel.deltaX = checkDeviceDeltaReturnValue ((float) getMsgSendFPRetFn() (ev, @selector (deviceDeltaX))); + wheel.deltaY = checkDeviceDeltaReturnValue ((float) getMsgSendFPRetFn() (ev, @selector (deviceDeltaY))); } } @catch (...) @@ -1483,7 +1491,7 @@ private: if ((! owner->textWasInserted) && (owner == nullptr || ! owner->redirectKeyDown (ev))) { objc_super s = { self, [NSView class] }; - objc_msgSendSuper (&s, @selector (keyDown:), ev); + getMsgSendSuperFn() (&s, @selector (keyDown:), ev); } } } @@ -1495,7 +1503,7 @@ private: if (owner == nullptr || ! owner->redirectKeyUp (ev)) { objc_super s = { self, [NSView class] }; - objc_msgSendSuper (&s, @selector (keyUp:), ev); + getMsgSendSuperFn() (&s, @selector (keyUp:), ev); } } @@ -1636,7 +1644,7 @@ private: return true; objc_super s = { self, [NSView class] }; - return objc_msgSendSuper (&s, @selector (performKeyEquivalent:), ev) != nil; + return getMsgSendSuperFn() (&s, @selector (performKeyEquivalent:), ev) != nil; } #endif @@ -1786,7 +1794,9 @@ private: static void windowDidExitFullScreen (id, SEL, NSNotification*) { + #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 [NSApp setPresentationOptions: NSApplicationPresentationDefault]; + #endif } static void zoom (id self, SEL, id sender) @@ -1795,7 +1805,7 @@ private: { owner->isZooming = true; objc_super s = { self, [NSWindow class] }; - objc_msgSendSuper (&s, @selector (zoom:), sender); + getMsgSendSuperFn() (&s, @selector (zoom:), sender); owner->isZooming = false; owner->redirectMovedOrResized(); diff --git a/source/modules/juce_gui_basics/native/juce_mac_Windowing.mm b/source/modules/juce_gui_basics/native/juce_mac_Windowing.mm index 8f5330459..11de42d59 100644 --- a/source/modules/juce_gui_basics/native/juce_mac_Windowing.mm +++ b/source/modules/juce_gui_basics/native/juce_mac_Windowing.mm @@ -45,9 +45,9 @@ public: { switch (getRawResult()) { - case NSAlertDefaultReturn: return 1; - case NSAlertOtherReturn: return 2; - default: return 0; + case NSAlertFirstButtonReturn: return 1; + case NSAlertThirdButtonReturn: return 2; + default: return 0; } } @@ -82,25 +82,26 @@ private: delete this; } - static NSString* translateIfNotNull (const char* s) + NSInteger getRawResult() const { - return s != nullptr ? juceStringToNS (TRANS (s)) : nil; + NSAlert* alert = [[[NSAlert alloc] init] autorelease]; + + [alert setMessageText: juceStringToNS (title)]; + [alert setInformativeText: juceStringToNS (message)]; + + [alert setAlertStyle: iconType == AlertWindow::WarningIcon ? NSCriticalAlertStyle + : NSInformationalAlertStyle]; + addButton (alert, button1); + addButton (alert, button2); + addButton (alert, button3); + + return [alert runModal]; } - NSInteger getRawResult() const + static void addButton (NSAlert* alert, const char* button) { - NSString* msg = juceStringToNS (message); - NSString* ttl = juceStringToNS (title); - NSString* b1 = translateIfNotNull (button1); - NSString* b2 = translateIfNotNull (button2); - NSString* b3 = translateIfNotNull (button3); - - switch (iconType) - { - case AlertWindow::InfoIcon: return NSRunInformationalAlertPanel (ttl, msg, b1, b2, b3); - case AlertWindow::WarningIcon: return NSRunCriticalAlertPanel (ttl, msg, b1, b2, b3); - default: return NSRunAlertPanel (ttl, msg, b1, b2, b3); - } + if (button != nullptr) + [alert addButtonWithTitle: juceStringToNS (TRANS (button))]; } }; @@ -164,37 +165,29 @@ bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& fi JUCE_AUTORELEASEPOOL { - NSView* view = (NSView*) sourceComp->getWindowHandle(); - - if (view == nil) - return false; - - NSPasteboard* pboard = [NSPasteboard pasteboardWithName: NSDragPboard]; - [pboard declareTypes: [NSArray arrayWithObject: NSFilenamesPboardType] - owner: nil]; - - NSMutableArray* filesArray = [NSMutableArray arrayWithCapacity: 4]; - for (int i = 0; i < files.size(); ++i) - [filesArray addObject: juceStringToNS (files[i])]; - - [pboard setPropertyList: filesArray - forType: NSFilenamesPboardType]; - - NSPoint dragPosition = [view convertPoint: [[[view window] currentEvent] locationInWindow] - fromView: nil]; - dragPosition.x -= 16; - dragPosition.y -= 16; - - [view dragImage: [[NSWorkspace sharedWorkspace] iconForFile: juceStringToNS (files[0])] - at: dragPosition - offset: NSMakeSize (0, 0) - event: [[view window] currentEvent] - pasteboard: pboard - source: view - slideBack: YES]; + if (NSView* view = (NSView*) sourceComp->getWindowHandle()) + { + if (NSEvent* event = [[view window] currentEvent]) + { + NSPoint eventPos = [event locationInWindow]; + NSRect dragRect = [view convertRect: NSMakeRect (eventPos.x - 16.0f, eventPos.y - 16.0f, 32.0f, 32.0f) + fromView: nil]; + + for (int i = 0; i < files.size(); ++i) + { + if (! [view dragFile: juceStringToNS (files[i]) + fromRect: dragRect + slideBack: YES + event: event]) + return false; + } + + return true; + } + } } - return true; + return false; } bool DragAndDropContainer::performExternalDragDropOfText (const String& /*text*/) @@ -340,13 +333,13 @@ public: const_cast (Desktop::getInstance().getDisplays()).refresh(); } - juce_DeclareSingleton_SingleThreaded_Minimal (DisplaySettingsChangeCallback); + juce_DeclareSingleton_SingleThreaded_Minimal (DisplaySettingsChangeCallback) private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DisplaySettingsChangeCallback) }; -juce_ImplementSingleton_SingleThreaded (DisplaySettingsChangeCallback); +juce_ImplementSingleton_SingleThreaded (DisplaySettingsChangeCallback) static Rectangle convertDisplayRect (NSRect r, CGFloat mainScreenBottom) { diff --git a/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp b/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp index 3ec007ccd..1796de478 100644 --- a/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp +++ b/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp @@ -1226,7 +1226,7 @@ private: LPCTSTR getWindowClassName() const noexcept { return (LPCTSTR) MAKELONG (atom, 0); } - juce_DeclareSingleton_SingleThreaded_Minimal (WindowClassHolder); + juce_DeclareSingleton_SingleThreaded_Minimal (WindowClassHolder) private: ATOM atom; @@ -1749,10 +1749,13 @@ private: doMouseMove (position); - updateModifiersFromWParam (wParam); - isDragging = true; + if (isValidPeer (this)) + { + updateModifiersFromWParam (wParam); + isDragging = true; - doMouseEvent (position); + doMouseEvent (position); + } } void doMouseUp (Point position, const WPARAM wParam) @@ -2936,7 +2939,7 @@ ComponentPeer* createNonRepaintingEmbeddedWindowsPeer (Component& component, voi } -juce_ImplementSingleton_SingleThreaded (HWNDComponentPeer::WindowClassHolder); +juce_ImplementSingleton_SingleThreaded (HWNDComponentPeer::WindowClassHolder) //============================================================================== @@ -3426,7 +3429,6 @@ void* MouseCursor::createStandardMouseCursor (const MouseCursor::StandardCursorT case IBeamCursor: cursorName = IDC_IBEAM; break; case PointingHandCursor: cursorName = MAKEINTRESOURCE(32649); break; case CrosshairCursor: cursorName = IDC_CROSS; break; - case CopyingCursor: break; // can't seem to find one of these in the system list.. case LeftRightResizeCursor: case LeftEdgeResizeCursor: @@ -3461,6 +3463,24 @@ void* MouseCursor::createStandardMouseCursor (const MouseCursor::StandardCursorT return dragHandCursor; } + case CopyingCursor: + { + static void* copyCursor = nullptr; + + if (copyCursor == nullptr) + { + static unsigned char copyCursorData[] = { 71,73,70,56,57,97,21,0,21,0,145,0,0,0,0,0,255,255,255,0, + 128,128,255,255,255,33,249,4,1,0,0,3,0,44,0,0,0,0,21,0, 21,0,0,2,72,4,134,169,171,16,199,98,11,79,90,71,161,93,56,111, + 78,133,218,215,137,31,82,154,100,200,86,91,202,142,12,108,212,87,235,174, 15,54,214,126,237,226,37,96,59,141,16,37,18,201,142,157,230,204,51,112, + 252,114,147,74,83,5,50,68,147,208,217,16,71,149,252,124,5,0,59,0,0 }; + const int copyCursorSize = 119; + + copyCursor = CustomMouseCursorInfo (ImageFileFormat::loadFrom (copyCursorData, copyCursorSize), 1, 3).create(); + } + + return copyCursor; + } + default: jassertfalse; break; } diff --git a/source/modules/juce_gui_basics/positioning/juce_RelativeRectangle.cpp b/source/modules/juce_gui_basics/positioning/juce_RelativeRectangle.cpp index 27f3eb618..1c706e0d9 100644 --- a/source/modules/juce_gui_basics/positioning/juce_RelativeRectangle.cpp +++ b/source/modules/juce_gui_basics/positioning/juce_RelativeRectangle.cpp @@ -208,7 +208,7 @@ public: void applyToComponentBounds() { - for (int i = 4; --i >= 0;) + for (int i = 32; --i >= 0;) { ComponentScope scope (getComponent()); const Rectangle newBounds (rectangle.resolve (&scope).getSmallestIntegerContainer()); diff --git a/source/modules/juce_gui_basics/positioning/juce_RelativeRectangle.h b/source/modules/juce_gui_basics/positioning/juce_RelativeRectangle.h index fc5ad456a..cd91c8dcf 100644 --- a/source/modules/juce_gui_basics/positioning/juce_RelativeRectangle.h +++ b/source/modules/juce_gui_basics/positioning/juce_RelativeRectangle.h @@ -28,7 +28,7 @@ //============================================================================== /** - An rectangle stored as a set of RelativeCoordinate values. + A rectangle stored as a set of RelativeCoordinate values. The rectangle's top, left, bottom and right edge positions are each stored as a RelativeCoordinate. diff --git a/source/modules/juce_gui_basics/widgets/juce_ComboBox.cpp b/source/modules/juce_gui_basics/widgets/juce_ComboBox.cpp index ffdcfd171..e49829d99 100644 --- a/source/modules/juce_gui_basics/widgets/juce_ComboBox.cpp +++ b/source/modules/juce_gui_basics/widgets/juce_ComboBox.cpp @@ -56,10 +56,7 @@ ComboBox::ComboBox (const String& name) ComboBox::~ComboBox() { currentId.removeListener (this); - - if (menuActive) - PopupMenu::dismissAllActiveMenus(); - + hidePopup(); label = nullptr; } @@ -473,7 +470,7 @@ bool ComboBox::keyPressed (const KeyPress& key) if (key == KeyPress::returnKey) { - showPopup(); + showPopupIfNotActive(); return true; } @@ -501,51 +498,70 @@ void ComboBox::labelTextChanged (Label*) //============================================================================== -void ComboBox::popupMenuFinishedCallback (int result, ComboBox* box) +void ComboBox::showPopupIfNotActive() { - if (box != nullptr) + if (! menuActive) { - box->menuActive = false; + menuActive = true; + showPopup(); + } +} - if (result != 0) - box->setSelectedId (result); +void ComboBox::hidePopup() +{ + if (menuActive) + { + menuActive = false; + PopupMenu::dismissAllActiveMenus(); + repaint(); } } -void ComboBox::showPopup() +static void comboBoxPopupMenuFinishedCallback (int result, ComboBox* combo) { - if (! menuActive) + if (combo != nullptr) { - const int selectedId = getSelectedId(); + combo->hidePopup(); - PopupMenu menu; - menu.setLookAndFeel (&getLookAndFeel()); + if (result != 0) + combo->setSelectedId (result); + } +} - for (int i = 0; i < items.size(); ++i) - { - const ItemInfo* const item = items.getUnchecked(i); - - if (item->isSeparator()) - menu.addSeparator(); - else if (item->isHeading) - menu.addSectionHeader (item->name); - else - menu.addItem (item->itemId, item->name, - item->isEnabled, item->itemId == selectedId); - } +void ComboBox::showPopup() +{ + PopupMenu menu; + menu.setLookAndFeel (&getLookAndFeel()); + addItemsToMenu (menu); - if (items.size() == 0) - menu.addItem (1, noChoicesMessage, false); + menu.showMenuAsync (PopupMenu::Options().withTargetComponent (this) + .withItemThatMustBeVisible (getSelectedId()) + .withMinimumWidth (getWidth()) + .withMaximumNumColumns (1) + .withStandardItemHeight (label->getHeight()), + ModalCallbackFunction::forComponent (comboBoxPopupMenuFinishedCallback, this)); +} - menuActive = true; +void ComboBox::addItemsToMenu (PopupMenu& menu) const +{ + const int selectedId = getSelectedId(); - menu.showMenuAsync (PopupMenu::Options().withTargetComponent (this) - .withItemThatMustBeVisible (selectedId) - .withMinimumWidth (getWidth()) - .withMaximumNumColumns (1) - .withStandardItemHeight (jlimit (12, 24, getHeight())), - ModalCallbackFunction::forComponent (popupMenuFinishedCallback, this)); + for (int i = 0; i < items.size(); ++i) + { + const ItemInfo* const item = items.getUnchecked(i); + jassert (item != nullptr); + + if (item->isSeparator()) + menu.addSeparator(); + else if (item->isHeading) + menu.addSectionHeader (item->name); + else + menu.addItem (item->itemId, item->name, + item->isEnabled, item->itemId == selectedId); } + + if (items.size() == 0) + menu.addItem (1, noChoicesMessage, false); } //============================================================================== @@ -556,7 +572,7 @@ void ComboBox::mouseDown (const MouseEvent& e) isButtonDown = isEnabled() && ! e.mods.isPopupMenu(); if (isButtonDown && (e.eventComponent == this || ! label->isEditable())) - showPopup(); + showPopupIfNotActive(); } void ComboBox::mouseDrag (const MouseEvent& e) @@ -564,7 +580,7 @@ void ComboBox::mouseDrag (const MouseEvent& e) beginDragAutoRepeat (50); if (isButtonDown && ! e.mouseWasClicked()) - showPopup(); + showPopupIfNotActive(); } void ComboBox::mouseUp (const MouseEvent& e2) @@ -579,7 +595,7 @@ void ComboBox::mouseUp (const MouseEvent& e2) if (reallyContains (e.getPosition(), true) && (e2.eventComponent == this || ! label->isEditable())) { - showPopup(); + showPopupIfNotActive(); } } } diff --git a/source/modules/juce_gui_basics/widgets/juce_ComboBox.h b/source/modules/juce_gui_basics/widgets/juce_ComboBox.h index 11515ba42..5a8c9484f 100644 --- a/source/modules/juce_gui_basics/widgets/juce_ComboBox.h +++ b/source/modules/juce_gui_basics/widgets/juce_ComboBox.h @@ -263,6 +263,15 @@ public: */ virtual void showPopup(); + /** Hides the combo box's popup list, if it's currently visible. */ + void hidePopup(); + + /** Returns true if the popup menu is currently being shown. */ + bool isPopupActive() const noexcept { return menuActive; } + + /** Adds the items in this ComboBox to the given menu. */ + virtual void addItemsToMenu (PopupMenu&) const; + //============================================================================== /** A class for receiving events from a ComboBox. @@ -423,7 +432,7 @@ private: int lastCurrentId; bool isButtonDown, separatorPending, menuActive, scrollWheelEnabled; float mouseWheelAccumulator; - ListenerList listeners; + ListenerList listeners; ScopedPointer