diff --git a/data/copy-juce-carla b/data/copy-juce-carla index 64b7312b3..1972f36c3 100755 --- a/data/copy-juce-carla +++ b/data/copy-juce-carla @@ -6,7 +6,7 @@ JUCE_MODULES_DIR="/home/falktx/Personal/FOSS/GIT/DISTRHO/libs/juce/source/module CARLA_MODULES_DIR="/home/falktx/Personal/FOSS/GIT/Carla/source/modules" # MODULES=("juce_audio_basics juce_audio_devices juce_audio_formats juce_audio_processors juce_core juce_data_structures juce_events juce_graphics juce_gui_basics") -MODULES=("juce_core") +MODULES=("juce_audio_basics") for M in $MODULES; do echo $M; diff --git a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp index 449e9ae41..9dda414fd 100644 --- a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp +++ b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp @@ -22,10 +22,13 @@ ============================================================================== */ -#if JUCE_USE_SSE_INTRINSICS - namespace FloatVectorHelpers { + + #define JUCE_INCREMENT_SRC_DEST dest += 4; src += 4; + #define JUCE_INCREMENT_DEST dest += 4; + + #if JUCE_USE_SSE_INTRINSICS static bool sse2Present = false; static bool isSSE2Available() noexcept @@ -44,7 +47,6 @@ namespace FloatVectorHelpers static inline float findMinimumOrMaximum (const float* src, int num, const bool isMinimum) noexcept { - #if JUCE_USE_SSE_INTRINSICS const int numLongOps = num / 4; if (numLongOps > 1 && FloatVectorHelpers::isSSE2Available()) @@ -90,66 +92,142 @@ namespace FloatVectorHelpers return localVal; } - #endif return isMinimum ? juce::findMinimum (src, num) : juce::findMaximum (src, num); } -} -#define JUCE_BEGIN_SSE_OP \ - if (FloatVectorHelpers::isSSE2Available()) \ - { \ + #define JUCE_BEGIN_SSE_OP \ + if (FloatVectorHelpers::isSSE2Available()) \ + { \ + const int numLongOps = num / 4; + + #define JUCE_FINISH_SSE_OP(normalOp) \ + num &= 3; \ + if (num == 0) return; \ + } \ + for (int i = 0; i < num; ++i) normalOp; + + #define JUCE_SSE_LOOP(sseOp, srcLoad, dstLoad, dstStore, locals, increment) \ + for (int i = 0; i < numLongOps; ++i) \ + { \ + locals (srcLoad, dstLoad); \ + dstStore (dest, sseOp); \ + increment; \ + } + + #define JUCE_LOAD_NONE(srcLoad, dstLoad) + #define JUCE_LOAD_DEST(srcLoad, dstLoad) const __m128 d = dstLoad (dest); + #define JUCE_LOAD_SRC(srcLoad, dstLoad) const __m128 s = srcLoad (src); + #define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const __m128 d = dstLoad (dest); const __m128 s = srcLoad (src); + + #define JUCE_PERFORM_SSE_OP_DEST(normalOp, sseOp, locals) \ + JUCE_BEGIN_SSE_OP \ + if (FloatVectorHelpers::isAligned (dest)) JUCE_SSE_LOOP (sseOp, dummy, _mm_load_ps, _mm_store_ps, locals, JUCE_INCREMENT_DEST) \ + else JUCE_SSE_LOOP (sseOp, dummy, _mm_loadu_ps, _mm_storeu_ps, locals, JUCE_INCREMENT_DEST) \ + JUCE_FINISH_SSE_OP (normalOp) + + #define JUCE_PERFORM_SSE_OP_SRC_DEST(normalOp, sseOp, locals, increment) \ + JUCE_BEGIN_SSE_OP \ + if (FloatVectorHelpers::isAligned (dest)) \ + { \ + if (FloatVectorHelpers::isAligned (src)) JUCE_SSE_LOOP (sseOp, _mm_load_ps, _mm_load_ps, _mm_store_ps, locals, increment) \ + else JUCE_SSE_LOOP (sseOp, _mm_loadu_ps, _mm_load_ps, _mm_store_ps, locals, increment) \ + }\ + else \ + { \ + if (FloatVectorHelpers::isAligned (src)) JUCE_SSE_LOOP (sseOp, _mm_load_ps, _mm_loadu_ps, _mm_storeu_ps, locals, increment) \ + else JUCE_SSE_LOOP (sseOp, _mm_loadu_ps, _mm_loadu_ps, _mm_storeu_ps, locals, increment) \ + } \ + JUCE_FINISH_SSE_OP (normalOp) + + + //============================================================================== + #elif JUCE_USE_ARM_NEON + + static inline float findMinimumOrMaximum (const float* src, int num, const bool isMinimum) noexcept + { + const int numLongOps = num / 4; + + if (numLongOps > 1) + { + float32x4_t val; + + #define JUCE_MINIMUMMAXIMUM_NEON_LOOP(loadOp, minMaxOp) \ + val = loadOp (src); \ + src += 4; \ + for (int i = 1; i < numLongOps; ++i) \ + { \ + const float32x4_t s = loadOp (src); \ + val = minMaxOp (val, s); \ + src += 4; \ + } + + if (isMinimum) { JUCE_MINIMUMMAXIMUM_NEON_LOOP (vld1q_f32, vminq_f32) } + else { JUCE_MINIMUMMAXIMUM_NEON_LOOP (vld1q_f32, vmaxq_f32) } + + float localVal; + + { + float vals[4]; + vst1q_f32 (vals, val); + + localVal = isMinimum ? jmin (vals[0], vals[1], vals[2], vals[3]) + : jmax (vals[0], vals[1], vals[2], vals[3]); + } + + num &= 3; + + for (int i = 0; i < num; ++i) + localVal = isMinimum ? jmin (localVal, src[i]) + : jmax (localVal, src[i]); + + return localVal; + } + + return isMinimum ? juce::findMinimum (src, num) + : juce::findMaximum (src, num); + } + + #define JUCE_BEGIN_NEON_OP \ const int numLongOps = num / 4; -#define JUCE_FINISH_SSE_OP(normalOp) \ + #define JUCE_FINISH_NEON_OP(normalOp) \ num &= 3; \ if (num == 0) return; \ - } \ - for (int i = 0; i < num; ++i) normalOp; - -#define JUCE_SSE_LOOP(sseOp, srcLoad, dstLoad, dstStore, locals, increment) \ - for (int i = 0; i < numLongOps; ++i) \ - { \ - locals (srcLoad, dstLoad); \ - dstStore (dest, sseOp); \ - increment; \ - } + for (int i = 0; i < num; ++i) normalOp; + + #define JUCE_NEON_LOOP(neonOp, srcLoad, dstLoad, dstStore, locals, increment) \ + for (int i = 0; i < numLongOps; ++i) \ + { \ + locals (srcLoad, dstLoad); \ + dstStore (dest, neonOp); \ + increment; \ + } -#define JUCE_INCREMENT_SRC_DEST dest += 4; src += 4; -#define JUCE_INCREMENT_DEST dest += 4; - -#define JUCE_LOAD_NONE(srcLoad, dstLoad) -#define JUCE_LOAD_DEST(srcLoad, dstLoad) const __m128 d = dstLoad (dest); -#define JUCE_LOAD_SRC(srcLoad, dstLoad) const __m128 s = srcLoad (src); -#define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const __m128 d = dstLoad (dest); const __m128 s = srcLoad (src); - -#define JUCE_PERFORM_SSE_OP_DEST(normalOp, sseOp, locals) \ - JUCE_BEGIN_SSE_OP \ - if (FloatVectorHelpers::isAligned (dest)) JUCE_SSE_LOOP (sseOp, dummy, _mm_load_ps, _mm_store_ps, locals, JUCE_INCREMENT_DEST) \ - else JUCE_SSE_LOOP (sseOp, dummy, _mm_loadu_ps, _mm_storeu_ps, locals, JUCE_INCREMENT_DEST) \ - JUCE_FINISH_SSE_OP (normalOp) - -#define JUCE_PERFORM_SSE_OP_SRC_DEST(normalOp, sseOp, locals, increment) \ - JUCE_BEGIN_SSE_OP \ - if (FloatVectorHelpers::isAligned (dest)) \ - { \ - if (FloatVectorHelpers::isAligned (src)) JUCE_SSE_LOOP (sseOp, _mm_load_ps, _mm_load_ps, _mm_store_ps, locals, increment) \ - else JUCE_SSE_LOOP (sseOp, _mm_loadu_ps, _mm_load_ps, _mm_store_ps, locals, increment) \ - }\ - else \ - { \ - if (FloatVectorHelpers::isAligned (src)) JUCE_SSE_LOOP (sseOp, _mm_load_ps, _mm_loadu_ps, _mm_storeu_ps, locals, increment) \ - else JUCE_SSE_LOOP (sseOp, _mm_loadu_ps, _mm_loadu_ps, _mm_storeu_ps, locals, increment) \ - } \ - JUCE_FINISH_SSE_OP (normalOp) - - -#else - #define JUCE_PERFORM_SSE_OP_DEST(normalOp, unused1, unused2) for (int i = 0; i < num; ++i) normalOp; - #define JUCE_PERFORM_SSE_OP_SRC_DEST(normalOp, sseOp, locals, increment) for (int i = 0; i < num; ++i) normalOp; -#endif + #define JUCE_LOAD_NONE(srcLoad, dstLoad) + #define JUCE_LOAD_DEST(srcLoad, dstLoad) const float32x4_t d = dstLoad (dest); + #define JUCE_LOAD_SRC(srcLoad, dstLoad) const float32x4_t s = srcLoad (src); + #define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const float32x4_t d = dstLoad (dest); const float32x4_t s = srcLoad (src); + + #define JUCE_PERFORM_NEON_OP_DEST(normalOp, neonOp, locals) \ + JUCE_BEGIN_NEON_OP \ + JUCE_NEON_LOOP (neonOp, dummy, vld1q_f32, vst1q_f32, locals, JUCE_INCREMENT_DEST) \ + JUCE_FINISH_NEON_OP (normalOp) + + #define JUCE_PERFORM_NEON_OP_SRC_DEST(normalOp, neonOp, locals) \ + JUCE_BEGIN_NEON_OP \ + JUCE_NEON_LOOP (neonOp, vld1q_f32, vld1q_f32, vst1q_f32, locals, JUCE_INCREMENT_SRC_DEST) \ + JUCE_FINISH_NEON_OP (normalOp) + + //============================================================================== + #else + #define JUCE_PERFORM_SSE_OP_DEST(normalOp, unused1, unused2) for (int i = 0; i < num; ++i) normalOp; + #define JUCE_PERFORM_SSE_OP_SRC_DEST(normalOp, sseOp, locals, increment) for (int i = 0; i < num; ++i) normalOp; + #endif +} +//============================================================================== void JUCE_CALLTYPE FloatVectorOperations::clear (float* dest, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK @@ -163,11 +241,13 @@ void JUCE_CALLTYPE FloatVectorOperations::fill (float* dest, float valueToFill, { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vfill (&valueToFill, dest, 1, (size_t) num); + #elif JUCE_USE_ARM_NEON + const float32x4_t val = vld1q_dup_f32 (&valueToFill); + JUCE_PERFORM_NEON_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE) #else #if JUCE_USE_SSE_INTRINSICS const __m128 val = _mm_load1_ps (&valueToFill); #endif - JUCE_PERFORM_SSE_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE) #endif } @@ -181,58 +261,78 @@ void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (float* dest, const f { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vsmul (src, 1, &multiplier, dest, 1, num); + #elif JUCE_USE_ARM_NEON + JUCE_PERFORM_NEON_OP_SRC_DEST (dest[i] += src[i], vmulq_n_f32(s, multiplier), JUCE_LOAD_SRC) #else #if JUCE_USE_SSE_INTRINSICS const __m128 mult = _mm_load1_ps (&multiplier); #endif - - JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] = src[i] * multiplier, - _mm_mul_ps (mult, s), + JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] = src[i] * multiplier, _mm_mul_ps (mult, s), JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST) #endif } +void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float amount, int num) noexcept +{ + #if JUCE_USE_ARM_NEON + const float32x4_t amountToAdd = vld1q_dup_f32(&amount); + JUCE_PERFORM_NEON_OP_DEST (dest[i] += amount, vaddq_f32 (d, amountToAdd), JUCE_LOAD_DEST) + #else + #if JUCE_USE_SSE_INTRINSICS + const __m128 amountToAdd = _mm_load1_ps (&amount); + #endif + JUCE_PERFORM_SSE_OP_DEST (dest[i] += amount, _mm_add_ps (d, amountToAdd), JUCE_LOAD_DEST) + #endif +} + void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vadd (src, 1, dest, 1, dest, 1, num); + #elif JUCE_USE_ARM_NEON + JUCE_PERFORM_NEON_OP_SRC_DEST (dest[i] += src[i], vaddq_f32 (d, s), JUCE_LOAD_SRC_DEST) #else - JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] += src[i], - _mm_add_ps (d, s), - JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST) + JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] += src[i], _mm_add_ps (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST) #endif } -void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float amount, int num) noexcept +void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* src, int num) noexcept { - #if JUCE_USE_SSE_INTRINSICS - const __m128 amountToAdd = _mm_load1_ps (&amount); + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsub (src, 1, dest, 1, dest, 1, num); + #elif JUCE_USE_ARM_NEON + JUCE_PERFORM_NEON_OP_SRC_DEST (dest[i] -= src[i], vsubq_f32 (d, s), JUCE_LOAD_SRC_DEST) + #else + JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] -= src[i], _mm_sub_ps (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST) #endif - - JUCE_PERFORM_SSE_OP_DEST (dest[i] += amount, - _mm_add_ps (d, amountToAdd), - JUCE_LOAD_DEST) } void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept { - #if JUCE_USE_SSE_INTRINSICS - const __m128 mult = _mm_load1_ps (&multiplier); + #if JUCE_USE_ARM_NEON + JUCE_PERFORM_NEON_OP_SRC_DEST (dest[i] += src[i] * multiplier, + vmlaq_n_f32 (d, s, multiplier), + JUCE_LOAD_SRC_DEST) + #else + #if JUCE_USE_SSE_INTRINSICS + const __m128 mult = _mm_load1_ps (&multiplier); + #endif + + JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] += src[i] * multiplier, + _mm_add_ps (d, _mm_mul_ps (mult, s)), + JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST) #endif - JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] += src[i] * multiplier, - _mm_add_ps (d, _mm_mul_ps (mult, s)), - JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST) } void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vmul (src, 1, dest, 1, dest, 1, num); + #elif JUCE_USE_ARM_NEON + JUCE_PERFORM_NEON_OP_SRC_DEST (dest[i] *= src[i], vmulq_f32 (d, s), JUCE_LOAD_SRC_DEST) #else - JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] *= src[i], - _mm_mul_ps (d, s), - JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST) + JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] *= src[i], _mm_mul_ps (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST) #endif } @@ -240,14 +340,13 @@ void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, float multiplie { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vsmul (dest, 1, &multiplier, dest, 1, num); + #elif JUCE_USE_ARM_NEON + JUCE_PERFORM_NEON_OP_DEST (dest[i] *= multiplier, vmulq_n_f32 (d, multiplier), JUCE_LOAD_DEST) #else #if JUCE_USE_SSE_INTRINSICS const __m128 mult = _mm_load1_ps (&multiplier); #endif - - JUCE_PERFORM_SSE_OP_DEST (dest[i] *= multiplier, - _mm_mul_ps (d, mult), - JUCE_LOAD_DEST) + JUCE_PERFORM_SSE_OP_DEST (dest[i] *= multiplier, _mm_mul_ps (d, mult), JUCE_LOAD_DEST) #endif } @@ -262,13 +361,19 @@ void FloatVectorOperations::negate (float* dest, const float* src, int num) noex void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, const int* src, float multiplier, int num) noexcept { - #if JUCE_USE_SSE_INTRINSICS - const __m128 mult = _mm_load1_ps (&multiplier); - #endif + #if JUCE_USE_ARM_NEON + JUCE_PERFORM_NEON_OP_SRC_DEST (dest[i] = src[i] * multiplier, + vmulq_n_f32 (vcvtq_f32_s32 (vld1q_s32 (src)), multiplier), + JUCE_LOAD_NONE) + #else + #if JUCE_USE_SSE_INTRINSICS + const __m128 mult = _mm_load1_ps (&multiplier); + #endif - JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] = src[i] * multiplier, - _mm_mul_ps (mult, _mm_cvtepi32_ps (_mm_loadu_si128 ((const __m128i*) src))), - JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST) + JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] = src[i] * multiplier, + _mm_mul_ps (mult, _mm_cvtepi32_ps (_mm_loadu_si128 ((const __m128i*) src))), + JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST) + #endif } void JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const float* src, int num, float& minResult, float& maxResult) noexcept @@ -315,6 +420,51 @@ void JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const float* src, int n localMax = jmax (localMax, s); } + minResult = localMin; + maxResult = localMax; + return; + } + #elif JUCE_USE_ARM_NEON + const int numLongOps = num / 4; + + if (numLongOps > 1) + { + float32x4_t mn, mx; + + #define JUCE_MINMAX_NEON_LOOP(loadOp) \ + mn = loadOp (src); \ + mx = mn; \ + src += 4; \ + for (int i = 1; i < numLongOps; ++i) \ + { \ + const float32x4_t s = loadOp (src); \ + mn = vminq_f32 (mn, s); \ + mx = vmaxq_f32 (mx, s); \ + src += 4; \ + } + + JUCE_MINMAX_NEON_LOOP (vld1q_f32); + + float localMin, localMax; + + { + float mns[4], mxs[4]; + vst1q_f32 (mns, mn); + vst1q_f32 (mxs, mx); + + localMin = jmin (mns[0], mns[1], mns[2], mns[3]); + localMax = jmax (mxs[0], mxs[1], mxs[2], mxs[3]); + } + + num &= 3; + + for (int i = 0; i < num; ++i) + { + const float s = src[i]; + localMin = jmin (localMin, s); + localMax = jmax (localMax, s); + } + minResult = localMin; maxResult = localMax; return; @@ -326,7 +476,7 @@ void JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const float* src, int n float JUCE_CALLTYPE FloatVectorOperations::findMinimum (const float* src, int num) noexcept { - #if JUCE_USE_SSE_INTRINSICS + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON return FloatVectorHelpers::findMinimumOrMaximum (src, num, true); #else return juce::findMinimum (src, num); @@ -335,7 +485,7 @@ float JUCE_CALLTYPE FloatVectorOperations::findMinimum (const float* src, int nu float JUCE_CALLTYPE FloatVectorOperations::findMaximum (const float* src, int num) noexcept { - #if JUCE_USE_SSE_INTRINSICS + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON return FloatVectorHelpers::findMinimumOrMaximum (src, num, false); #else return juce::findMaximum (src, num); @@ -350,3 +500,129 @@ void JUCE_CALLTYPE FloatVectorOperations::enableFlushToZeroMode (bool shouldEnab #endif (void) shouldEnable; } + +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +class FloatVectorOperationsTests : public UnitTest +{ +public: + FloatVectorOperationsTests() : UnitTest ("FloatVectorOperations") {} + + void runTest() + { + beginTest ("FloatVectorOperations"); + + for (int i = 100; --i >= 0;) + { + const int num = getRandom().nextInt (500) + 1; + + HeapBlock buffer1 (num + 16), buffer2 (num + 16); + HeapBlock buffer3 (num + 16); + + #if JUCE_ARM + float* const data1 = buffer1; + float* const data2 = buffer2; + int* const int1 = buffer3; + #else + float* const data1 = addBytesToPointer (buffer1.getData(), getRandom().nextInt (16)); + float* const data2 = addBytesToPointer (buffer2.getData(), getRandom().nextInt (16)); + int* const int1 = addBytesToPointer (buffer3.getData(), getRandom().nextInt (16)); + #endif + + fillRandomly (data1, num); + fillRandomly (data2, num); + + float mn1, mx1, mn2, mx2; + FloatVectorOperations::findMinAndMax (data1, num, mn1, mx1); + juce::findMinAndMax (data1, num, mn2, mx2); + expect (mn1 == mn2); + expect (mx1 == mx2); + + expect (FloatVectorOperations::findMinimum (data1, num) == juce::findMinimum (data1, num)); + expect (FloatVectorOperations::findMaximum (data1, num) == juce::findMaximum (data1, num)); + + expect (FloatVectorOperations::findMinimum (data2, num) == juce::findMinimum (data2, num)); + expect (FloatVectorOperations::findMaximum (data2, num) == juce::findMaximum (data2, num)); + + FloatVectorOperations::clear (data1, num); + expect (areAllValuesEqual (data1, num, 0)); + + FloatVectorOperations::fill (data1, 2.0f, num); + expect (areAllValuesEqual (data1, num, 2.0f)); + + FloatVectorOperations::add (data1, 2.0f, num); + expect (areAllValuesEqual (data1, num, 4.0f)); + + FloatVectorOperations::copy (data2, data1, num); + expect (areAllValuesEqual (data2, num, 4.0f)); + + FloatVectorOperations::add (data2, data1, num); + expect (areAllValuesEqual (data2, num, 8.0f)); + + FloatVectorOperations::copyWithMultiply (data2, data1, 4.0f, num); + expect (areAllValuesEqual (data2, num, 16.0f)); + + FloatVectorOperations::addWithMultiply (data2, data1, 4.0f, num); + expect (areAllValuesEqual (data2, num, 32.0f)); + + FloatVectorOperations::multiply (data1, 2.0f, num); + expect (areAllValuesEqual (data1, num, 8.0f)); + + FloatVectorOperations::multiply (data1, data2, num); + expect (areAllValuesEqual (data1, num, 256.0f)); + + FloatVectorOperations::negate (data2, data1, num); + expect (areAllValuesEqual (data2, num, -256.0f)); + + FloatVectorOperations::subtract (data1, data2, num); + expect (areAllValuesEqual (data1, num, 512.0f)); + + fillRandomly (int1, num); + FloatVectorOperations::convertFixedToFloat (data1, int1, 2.0f, num); + convertFixed (data2, int1, 2.0f, num); + expect (buffersMatch (data1, data2, num)); + } + } + + void fillRandomly (float* d, int num) + { + while (--num >= 0) + *d++ = getRandom().nextFloat() * 1000.0f; + } + + void fillRandomly (int* d, int num) + { + while (--num >= 0) + *d++ = getRandom().nextInt(); + } + + static void convertFixed (float* d, const int* s, float multiplier, int num) + { + while (--num >= 0) + *d++ = *s++ * multiplier; + } + + static bool areAllValuesEqual (const float* d, int num, float target) + { + while (--num >= 0) + if (*d++ != target) + return false; + + return true; + } + + static bool buffersMatch (const float* d1, const float* d2, int num) + { + while (--num >= 0) + if (std::abs (*d1++ - *d2++) > std::numeric_limits::epsilon()) + return false; + + return true; + } +}; + +static FloatVectorOperationsTests vectorOpTests; + +#endif diff --git a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h index 6268ab913..6ffcfab37 100644 --- a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h +++ b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h @@ -47,11 +47,14 @@ public: /** Copies a vector of floats, multiplying each value by a given multiplier */ static void JUCE_CALLTYPE copyWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept; + /** Adds a fixed value to the destination values. */ + static void JUCE_CALLTYPE add (float* dest, float amount, int numValues) noexcept; + /** Adds the source values to the destination values. */ static void JUCE_CALLTYPE add (float* dest, const float* src, int numValues) noexcept; - /** Adds a fixed value to the destination values. */ - static void JUCE_CALLTYPE add (float* dest, float amount, int numValues) noexcept; + /** Subtracts the source values from the destination values. */ + static void JUCE_CALLTYPE subtract (float* dest, const float* src, int numValues) noexcept; /** Multiplies each source value by the given multiplier, then adds it to the destination value. */ static void JUCE_CALLTYPE addWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept; diff --git a/source/modules/juce_audio_basics/juce_audio_basics.cpp b/source/modules/juce_audio_basics/juce_audio_basics.cpp index 2bdd5a490..d8e5b9b1f 100644 --- a/source/modules/juce_audio_basics/juce_audio_basics.cpp +++ b/source/modules/juce_audio_basics/juce_audio_basics.cpp @@ -58,6 +58,11 @@ #undef JUCE_USE_VDSP_FRAMEWORK #endif +#if __ARM_NEON__ && ! (JUCE_USE_VDSP_FRAMEWORK || defined (JUCE_USE_ARM_NEON)) + #define JUCE_USE_ARM_NEON 1 + #include +#endif + namespace juce { diff --git a/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp b/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp index dbed83af9..c11140450 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp +++ b/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp @@ -39,8 +39,7 @@ namespace MidiHelpers int MidiMessage::readVariableLengthVal (const uint8* data, int& numBytesUsed) noexcept { numBytesUsed = 0; - int v = 0; - int i; + int v = 0, i; do { @@ -76,28 +75,9 @@ int MidiMessage::getMessageLengthFromFirstByte (const uint8 firstByte) noexcept return messageLengths [firstByte & 0x7f]; } -//============================================================================== -inline void MidiMessage::setToUseInternalData() noexcept -{ - data = static_cast (preallocatedData.asBytes); -} - -inline bool MidiMessage::usesAllocatedData() const noexcept -{ - return data != static_cast (preallocatedData.asBytes); -} - -inline void MidiMessage::freeData() noexcept -{ - if (usesAllocatedData()) - delete[] data; -} - //============================================================================== MidiMessage::MidiMessage() noexcept - : timeStamp (0), - data (static_cast (preallocatedData.asBytes)), - size (2) + : timeStamp (0), size (2) { preallocatedData.asBytes[0] = 0xf0; preallocatedData.asBytes[1] = 0xf7; @@ -108,22 +88,14 @@ MidiMessage::MidiMessage (const void* const d, const int dataSize, const double size (dataSize) { jassert (dataSize > 0); - - if (dataSize <= 4) - setToUseInternalData(); - else - data = new uint8 [dataSize]; - - memcpy (data, d, (size_t) dataSize); + memcpy (allocateSpace (dataSize), d, (size_t) dataSize); // check that the length matches the data.. - jassert (size > 3 || data[0] >= 0xf0 || getMessageLengthFromFirstByte (data[0]) == size); + jassert (size > 3 || *(uint8*)d >= 0xf0 || getMessageLengthFromFirstByte (*(uint8*)d) == size); } MidiMessage::MidiMessage (const int byte1, const double t) noexcept - : timeStamp (t), - data (static_cast (preallocatedData.asBytes)), - size (1) + : timeStamp (t), size (1) { preallocatedData.asBytes[0] = (uint8) byte1; @@ -132,9 +104,7 @@ MidiMessage::MidiMessage (const int byte1, const double t) noexcept } MidiMessage::MidiMessage (const int byte1, const int byte2, const double t) noexcept - : timeStamp (t), - data (static_cast (preallocatedData.asBytes)), - size (2) + : timeStamp (t), size (2) { preallocatedData.asBytes[0] = (uint8) byte1; preallocatedData.asBytes[1] = (uint8) byte2; @@ -144,9 +114,7 @@ MidiMessage::MidiMessage (const int byte1, const int byte2, const double t) noex } MidiMessage::MidiMessage (const int byte1, const int byte2, const int byte3, const double t) noexcept - : timeStamp (t), - data (static_cast (preallocatedData.asBytes)), - size (3) + : timeStamp (t), size (3) { preallocatedData.asBytes[0] = (uint8) byte1; preallocatedData.asBytes[1] = (uint8) byte2; @@ -157,42 +125,37 @@ MidiMessage::MidiMessage (const int byte1, const int byte2, const int byte3, con } MidiMessage::MidiMessage (const MidiMessage& other) - : timeStamp (other.timeStamp), - size (other.size) + : timeStamp (other.timeStamp), size (other.size) { - if (other.usesAllocatedData()) + if (other.allocatedData != nullptr) { - data = new uint8 [size]; - memcpy (data, other.data, (size_t) size); + allocatedData.malloc (size); + memcpy (allocatedData, other.allocatedData, (size_t) size); } else { - setToUseInternalData(); preallocatedData.asInt32 = other.preallocatedData.asInt32; } } MidiMessage::MidiMessage (const MidiMessage& other, const double newTimeStamp) - : timeStamp (newTimeStamp), - size (other.size) + : timeStamp (newTimeStamp), size (other.size) { - if (other.usesAllocatedData()) + if (other.allocatedData != nullptr) { - data = new uint8 [size]; - memcpy (data, other.data, (size_t) size); + allocatedData.malloc (size); + memcpy (allocatedData, other.allocatedData, (size_t) size); } else { - setToUseInternalData(); preallocatedData.asInt32 = other.preallocatedData.asInt32; } } -MidiMessage::MidiMessage (const void* src_, int sz, int& numBytesUsed, const uint8 lastStatusByte, double t) - : timeStamp (t), - data (static_cast (preallocatedData.asBytes)) +MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const uint8 lastStatusByte, double t) + : timeStamp (t) { - const uint8* src = static_cast (src_); + const uint8* src = static_cast (srcData); unsigned int byte = (unsigned int) *src; if (byte < 0x80) @@ -229,11 +192,8 @@ MidiMessage::MidiMessage (const void* src_, int sz, int& numBytesUsed, const uin break; // bytes, assume it's the end of the sysex ++numVariableLengthSysexBytes; - ++d; - continue; } - - if (! haveReadAllLengthBytes) + else if (! haveReadAllLengthBytes) { haveReadAllLengthBytes = true; ++numVariableLengthSysexBytes; @@ -242,11 +202,14 @@ MidiMessage::MidiMessage (const void* src_, int sz, int& numBytesUsed, const uin ++d; } + src += numVariableLengthSysexBytes; size = 1 + (int) (d - src); - data = new uint8 [size - numVariableLengthSysexBytes]; - *data = (uint8) byte; - memcpy (data + 1, src + numVariableLengthSysexBytes, (size_t) (size - numVariableLengthSysexBytes - 1)); + uint8* dest = allocateSpace (size); + *dest = (uint8) byte; + memcpy (dest + 1, src, (size_t) (size - 1)); + + numBytesUsed += numVariableLengthSysexBytes; // (these aren't counted in the size) } else if (byte == 0xff) { @@ -254,22 +217,22 @@ MidiMessage::MidiMessage (const void* src_, int sz, int& numBytesUsed, const uin const int bytesLeft = readVariableLengthVal (src + 1, n); size = jmin (sz + 1, n + 2 + bytesLeft); - data = new uint8 [size]; - *data = (uint8) byte; - memcpy (data + 1, src, (size_t) size - 1); + uint8* dest = allocateSpace (size); + *dest = (uint8) byte; + memcpy (dest + 1, src, (size_t) size - 1); } else { preallocatedData.asInt32 = 0; size = getMessageLengthFromFirstByte ((uint8) byte); - data[0] = (uint8) byte; + preallocatedData.asBytes[0] = (uint8) byte; if (size > 1) { - data[1] = src[0]; + preallocatedData.asBytes[1] = src[0]; if (size > 2) - data[2] = src[1]; + preallocatedData.asBytes[2] = src[1]; } } @@ -289,16 +252,14 @@ MidiMessage& MidiMessage::operator= (const MidiMessage& other) timeStamp = other.timeStamp; size = other.size; - freeData(); - - if (other.usesAllocatedData()) + if (other.allocatedData != nullptr) { - data = new uint8 [size]; - memcpy (data, other.data, (size_t) size); + allocatedData.malloc (size); + memcpy (allocatedData, other.allocatedData, (size_t) size); } else { - setToUseInternalData(); + allocatedData.free(); preallocatedData.asInt32 = other.preallocatedData.asInt32; } } @@ -308,19 +269,12 @@ MidiMessage& MidiMessage::operator= (const MidiMessage& other) #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS MidiMessage::MidiMessage (MidiMessage&& other) noexcept - : timeStamp (other.timeStamp), - size (other.size) + : timeStamp (other.timeStamp), size (other.size) { - if (other.usesAllocatedData()) - { - data = other.data; - other.setToUseInternalData(); - } + if (other.allocatedData != nullptr) + allocatedData.swapWith (other.allocatedData); else - { - setToUseInternalData(); preallocatedData.asInt32 = other.preallocatedData.asInt32; - } } MidiMessage& MidiMessage::operator= (MidiMessage&& other) noexcept @@ -329,31 +283,30 @@ MidiMessage& MidiMessage::operator= (MidiMessage&& other) noexcept timeStamp = other.timeStamp; size = other.size; - - freeData(); - - if (other.usesAllocatedData()) - { - data = other.data; - other.setToUseInternalData(); - } - else - { - setToUseInternalData(); - preallocatedData.asInt32 = other.preallocatedData.asInt32; - } + allocatedData.swapWith (other.allocatedData); + preallocatedData.asInt32 = other.preallocatedData.asInt32; return *this; } #endif -MidiMessage::~MidiMessage() +MidiMessage::~MidiMessage() {} + +uint8* MidiMessage::allocateSpace (int bytes) { - freeData(); + if (bytes > 4) + { + allocatedData.malloc (bytes); + return allocatedData; + } + + return preallocatedData.asBytes; } int MidiMessage::getChannel() const noexcept { + const uint8* const data = getRawData(); + if ((data[0] & 0xf0) != 0xf0) return (data[0] & 0xf) + 1; @@ -364,6 +317,8 @@ bool MidiMessage::isForChannel (const int channel) const noexcept { jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16 + const uint8* const data = getRawData(); + return ((data[0] & 0xf) == channel - 1) && ((data[0] & 0xf0) != 0xf0); } @@ -372,6 +327,8 @@ void MidiMessage::setChannel (const int channel) noexcept { jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16 + uint8* const data = getData(); + if ((data[0] & 0xf0) != (uint8) 0xf0) data[0] = (uint8) ((data[0] & (uint8) 0xf0) | (uint8)(channel - 1)); @@ -379,37 +336,43 @@ void MidiMessage::setChannel (const int channel) noexcept bool MidiMessage::isNoteOn (const bool returnTrueForVelocity0) const noexcept { + const uint8* const data = getRawData(); + return ((data[0] & 0xf0) == 0x90) && (returnTrueForVelocity0 || data[2] != 0); } bool MidiMessage::isNoteOff (const bool returnTrueForNoteOnVelocity0) const noexcept { + const uint8* const data = getRawData(); + return ((data[0] & 0xf0) == 0x80) || (returnTrueForNoteOnVelocity0 && (data[2] == 0) && ((data[0] & 0xf0) == 0x90)); } bool MidiMessage::isNoteOnOrOff() const noexcept { + const uint8* const data = getRawData(); + const int d = data[0] & 0xf0; return (d == 0x90) || (d == 0x80); } int MidiMessage::getNoteNumber() const noexcept { - return data[1]; + return getRawData()[1]; } void MidiMessage::setNoteNumber (const int newNoteNumber) noexcept { if (isNoteOnOrOff()) - data[1] = (uint8) (newNoteNumber & 127); + getData()[1] = (uint8) (newNoteNumber & 127); } uint8 MidiMessage::getVelocity() const noexcept { if (isNoteOnOrOff()) - return data[2]; + return getRawData()[2]; return 0; } @@ -422,24 +385,27 @@ float MidiMessage::getFloatVelocity() const noexcept void MidiMessage::setVelocity (const float newVelocity) noexcept { if (isNoteOnOrOff()) - data[2] = MidiHelpers::validVelocity (roundToInt (newVelocity * 127.0f)); + getData()[2] = MidiHelpers::validVelocity (roundToInt (newVelocity * 127.0f)); } void MidiMessage::multiplyVelocity (const float scaleFactor) noexcept { if (isNoteOnOrOff()) + { + uint8* const data = getData(); data[2] = MidiHelpers::validVelocity (roundToInt (scaleFactor * data[2])); + } } bool MidiMessage::isAftertouch() const noexcept { - return (data[0] & 0xf0) == 0xa0; + return (getRawData()[0] & 0xf0) == 0xa0; } int MidiMessage::getAfterTouchValue() const noexcept { jassert (isAftertouch()); - return data[2]; + return getRawData()[2]; } MidiMessage MidiMessage::aftertouchChange (const int channel, @@ -457,13 +423,13 @@ MidiMessage MidiMessage::aftertouchChange (const int channel, bool MidiMessage::isChannelPressure() const noexcept { - return (data[0] & 0xf0) == 0xd0; + return (getRawData()[0] & 0xf0) == 0xd0; } int MidiMessage::getChannelPressureValue() const noexcept { jassert (isChannelPressure()); - return data[1]; + return getRawData()[1]; } MidiMessage MidiMessage::channelPressureChange (const int channel, const int pressure) noexcept @@ -474,25 +440,25 @@ MidiMessage MidiMessage::channelPressureChange (const int channel, const int pre return MidiMessage (MidiHelpers::initialByte (0xd0, channel), pressure & 0x7f); } -bool MidiMessage::isSustainPedalOn() const noexcept { return isControllerOfType (0x40) && data[2] >= 64; } -bool MidiMessage::isSustainPedalOff() const noexcept { return isControllerOfType (0x40) && data[2] < 64; } +bool MidiMessage::isSustainPedalOn() const noexcept { return isControllerOfType (0x40) && getRawData()[2] >= 64; } +bool MidiMessage::isSustainPedalOff() const noexcept { return isControllerOfType (0x40) && getRawData()[2] < 64; } -bool MidiMessage::isSostenutoPedalOn() const noexcept { return isControllerOfType (0x42) && data[2] >= 64; } -bool MidiMessage::isSostenutoPedalOff() const noexcept { return isControllerOfType (0x42) && data[2] < 64; } +bool MidiMessage::isSostenutoPedalOn() const noexcept { return isControllerOfType (0x42) && getRawData()[2] >= 64; } +bool MidiMessage::isSostenutoPedalOff() const noexcept { return isControllerOfType (0x42) && getRawData()[2] < 64; } -bool MidiMessage::isSoftPedalOn() const noexcept { return isControllerOfType (0x43) && data[2] >= 64; } -bool MidiMessage::isSoftPedalOff() const noexcept { return isControllerOfType (0x43) && data[2] < 64; } +bool MidiMessage::isSoftPedalOn() const noexcept { return isControllerOfType (0x43) && getRawData()[2] >= 64; } +bool MidiMessage::isSoftPedalOff() const noexcept { return isControllerOfType (0x43) && getRawData()[2] < 64; } bool MidiMessage::isProgramChange() const noexcept { - return (data[0] & 0xf0) == 0xc0; + return (getRawData()[0] & 0xf0) == 0xc0; } int MidiMessage::getProgramChangeNumber() const noexcept { jassert (isProgramChange()); - return data[1]; + return getRawData()[1]; } MidiMessage MidiMessage::programChange (const int channel, const int programNumber) noexcept @@ -504,12 +470,13 @@ MidiMessage MidiMessage::programChange (const int channel, const int programNumb bool MidiMessage::isPitchWheel() const noexcept { - return (data[0] & 0xf0) == 0xe0; + return (getRawData()[0] & 0xf0) == 0xe0; } int MidiMessage::getPitchWheelValue() const noexcept { jassert (isPitchWheel()); + const uint8* const data = getRawData(); return data[1] | (data[2] << 7); } @@ -524,24 +491,25 @@ MidiMessage MidiMessage::pitchWheel (const int channel, const int position) noex bool MidiMessage::isController() const noexcept { - return (data[0] & 0xf0) == 0xb0; + return (getRawData()[0] & 0xf0) == 0xb0; } bool MidiMessage::isControllerOfType (const int controllerType) const noexcept { + const uint8* const data = getRawData(); return (data[0] & 0xf0) == 0xb0 && data[1] == controllerType; } int MidiMessage::getControllerNumber() const noexcept { jassert (isController()); - return data[1]; + return getRawData()[1]; } int MidiMessage::getControllerValue() const noexcept { jassert (isController()); - return data[2]; + return getRawData()[2]; } MidiMessage MidiMessage::controllerEvent (const int channel, const int controllerType, const int value) noexcept @@ -583,6 +551,7 @@ MidiMessage MidiMessage::allNotesOff (const int channel) noexcept bool MidiMessage::isAllNotesOff() const noexcept { + const uint8* const data = getRawData(); return (data[0] & 0xf0) == 0xb0 && data[1] == 123; } @@ -593,6 +562,7 @@ MidiMessage MidiMessage::allSoundOff (const int channel) noexcept bool MidiMessage::isAllSoundOff() const noexcept { + const uint8* const data = getRawData(); return (data[0] & 0xf0) == 0xb0 && data[1] == 120; } @@ -616,7 +586,7 @@ MidiMessage MidiMessage::masterVolume (const float volume) //============================================================================== bool MidiMessage::isSysEx() const noexcept { - return *data == 0xf0; + return *getRawData() == 0xf0; } MidiMessage MidiMessage::createSysExMessage (const void* sysexData, const int dataSize) @@ -641,16 +611,18 @@ int MidiMessage::getSysExDataSize() const noexcept } //============================================================================== -bool MidiMessage::isMetaEvent() const noexcept { return *data == 0xff; } -bool MidiMessage::isActiveSense() const noexcept { return *data == 0xfe; } +bool MidiMessage::isMetaEvent() const noexcept { return *getRawData() == 0xff; } +bool MidiMessage::isActiveSense() const noexcept { return *getRawData() == 0xfe; } int MidiMessage::getMetaEventType() const noexcept { + const uint8* const data = getRawData(); return *data != 0xff ? -1 : data[1]; } int MidiMessage::getMetaEventLength() const noexcept { + const uint8* const data = getRawData(); if (*data == 0xff) { int n; @@ -665,7 +637,7 @@ const uint8* MidiMessage::getMetaEventData() const noexcept jassert (isMetaEvent()); int n; - const uint8* d = data + 2; + const uint8* d = getRawData() + 2; readVariableLengthVal (d, n); return d + n; } @@ -686,14 +658,14 @@ String MidiMessage::getTextFromTextMetaEvent() const CharPointer_UTF8 (textData + getMetaEventLength())); } -bool MidiMessage::isTrackNameEvent() const noexcept { return (data[1] == 3) && (*data == 0xff); } -bool MidiMessage::isTempoMetaEvent() const noexcept { return (data[1] == 81) && (*data == 0xff); } -bool MidiMessage::isMidiChannelMetaEvent() const noexcept { return (data[1] == 0x20) && (*data == 0xff) && (data[2] == 1); } +bool MidiMessage::isTrackNameEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 3) && (*data == 0xff); } +bool MidiMessage::isTempoMetaEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 81) && (*data == 0xff); } +bool MidiMessage::isMidiChannelMetaEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 0x20) && (*data == 0xff) && (data[2] == 1); } int MidiMessage::getMidiChannelMetaEventChannel() const noexcept { jassert (isMidiChannelMetaEvent()); - return data[3] + 1; + return getRawData()[3] + 1; } double MidiMessage::getTempoSecondsPerQuarterNote() const noexcept @@ -748,6 +720,7 @@ MidiMessage MidiMessage::tempoMetaEvent (int microsecondsPerQuarterNote) noexcep bool MidiMessage::isTimeSignatureMetaEvent() const noexcept { + const uint8* const data = getRawData(); return (data[1] == 0x58) && (*data == (uint8) 0xff); } @@ -816,8 +789,8 @@ MidiMessage MidiMessage::endOfTrack() noexcept } //============================================================================== -bool MidiMessage::isSongPositionPointer() const noexcept { return *data == 0xf2; } -int MidiMessage::getSongPositionPointerMidiBeat() const noexcept { return data[1] | (data[2] << 7); } +bool MidiMessage::isSongPositionPointer() const noexcept { return *getRawData() == 0xf2; } +int MidiMessage::getSongPositionPointerMidiBeat() const noexcept { const uint8* data = getRawData(); return data[1] | (data[2] << 7); } MidiMessage MidiMessage::songPositionPointer (const int positionInMidiBeats) noexcept { @@ -826,21 +799,21 @@ MidiMessage MidiMessage::songPositionPointer (const int positionInMidiBeats) noe (positionInMidiBeats >> 7) & 127); } -bool MidiMessage::isMidiStart() const noexcept { return *data == 0xfa; } +bool MidiMessage::isMidiStart() const noexcept { return *getRawData() == 0xfa; } MidiMessage MidiMessage::midiStart() noexcept { return MidiMessage (0xfa); } -bool MidiMessage::isMidiContinue() const noexcept { return *data == 0xfb; } +bool MidiMessage::isMidiContinue() const noexcept { return *getRawData() == 0xfb; } MidiMessage MidiMessage::midiContinue() noexcept { return MidiMessage (0xfb); } -bool MidiMessage::isMidiStop() const noexcept { return *data == 0xfc; } +bool MidiMessage::isMidiStop() const noexcept { return *getRawData() == 0xfc; } MidiMessage MidiMessage::midiStop() noexcept { return MidiMessage (0xfc); } -bool MidiMessage::isMidiClock() const noexcept { return *data == 0xf8; } +bool MidiMessage::isMidiClock() const noexcept { return *getRawData() == 0xf8; } MidiMessage MidiMessage::midiClock() noexcept { return MidiMessage (0xf8); } -bool MidiMessage::isQuarterFrame() const noexcept { return *data == 0xf1; } -int MidiMessage::getQuarterFrameSequenceNumber() const noexcept { return ((int) data[1]) >> 4; } -int MidiMessage::getQuarterFrameValue() const noexcept { return ((int) data[1]) & 0x0f; } +bool MidiMessage::isQuarterFrame() const noexcept { return *getRawData() == 0xf1; } +int MidiMessage::getQuarterFrameSequenceNumber() const noexcept { return ((int) getRawData()[1]) >> 4; } +int MidiMessage::getQuarterFrameValue() const noexcept { return ((int) getRawData()[1]) & 0x0f; } MidiMessage MidiMessage::quarterFrame (const int sequenceNumber, const int value) noexcept { @@ -849,6 +822,8 @@ MidiMessage MidiMessage::quarterFrame (const int sequenceNumber, const int value bool MidiMessage::isFullFrame() const noexcept { + const uint8* const data = getRawData(); + return data[0] == 0xf0 && data[1] == 0x7f && size >= 10 @@ -861,6 +836,7 @@ void MidiMessage::getFullFrameParameters (int& hours, int& minutes, int& seconds { jassert (isFullFrame()); + const uint8* const data = getRawData(); timecodeType = (SmpteTimecodeType) (data[5] >> 5); hours = data[5] & 0x1f; minutes = data[6]; @@ -884,6 +860,7 @@ MidiMessage MidiMessage::fullFrame (const int hours, const int minutes, bool MidiMessage::isMidiMachineControlMessage() const noexcept { + const uint8* const data = getRawData(); return data[0] == 0xf0 && data[1] == 0x7f && data[3] == 0x06 @@ -894,7 +871,7 @@ MidiMessage::MidiMachineControlCommand MidiMessage::getMidiMachineControlCommand { jassert (isMidiMachineControlMessage()); - return (MidiMachineControlCommand) data[4]; + return (MidiMachineControlCommand) getRawData()[4]; } MidiMessage MidiMessage::midiMachineControlCommand (MidiMessage::MidiMachineControlCommand command) @@ -907,6 +884,7 @@ MidiMessage MidiMessage::midiMachineControlCommand (MidiMessage::MidiMachineCont //============================================================================== bool MidiMessage::isMidiMachineControlGoto (int& hours, int& minutes, int& seconds, int& frames) const noexcept { + const uint8* const data = getRawData(); if (size >= 12 && data[0] == 0xf0 && data[1] == 0x7f @@ -967,29 +945,38 @@ const char* MidiMessage::getGMInstrumentName (const int n) { static const char* names[] = { - "Acoustic Grand Piano", "Bright Acoustic Piano", "Electric Grand Piano", "Honky-tonk Piano", - "Electric Piano 1", "Electric Piano 2", "Harpsichord", "Clavinet", "Celesta", "Glockenspiel", - "Music Box", "Vibraphone", "Marimba", "Xylophone", "Tubular Bells", "Dulcimer", "Drawbar Organ", - "Percussive Organ", "Rock Organ", "Church Organ", "Reed Organ", "Accordion", "Harmonica", - "Tango Accordion", "Acoustic Guitar (nylon)", "Acoustic Guitar (steel)", "Electric Guitar (jazz)", - "Electric Guitar (clean)", "Electric Guitar (mute)", "Overdriven Guitar", "Distortion Guitar", - "Guitar Harmonics", "Acoustic Bass", "Electric Bass (finger)", "Electric Bass (pick)", - "Fretless Bass", "Slap Bass 1", "Slap Bass 2", "Synth Bass 1", "Synth Bass 2", "Violin", - "Viola", "Cello", "Contrabass", "Tremolo Strings", "Pizzicato Strings", "Orchestral Harp", - "Timpani", "String Ensemble 1", "String Ensemble 2", "SynthStrings 1", "SynthStrings 2", - "Choir Aahs", "Voice Oohs", "Synth Voice", "Orchestra Hit", "Trumpet", "Trombone", "Tuba", - "Muted Trumpet", "French Horn", "Brass Section", "SynthBrass 1", "SynthBrass 2", "Soprano Sax", - "Alto Sax", "Tenor Sax", "Baritone Sax", "Oboe", "English Horn", "Bassoon", "Clarinet", - "Piccolo", "Flute", "Recorder", "Pan Flute", "Blown Bottle", "Shakuhachi", "Whistle", - "Ocarina", "Lead 1 (square)", "Lead 2 (sawtooth)", "Lead 3 (calliope)", "Lead 4 (chiff)", - "Lead 5 (charang)", "Lead 6 (voice)", "Lead 7 (fifths)", "Lead 8 (bass+lead)", "Pad 1 (new age)", - "Pad 2 (warm)", "Pad 3 (polysynth)", "Pad 4 (choir)", "Pad 5 (bowed)", "Pad 6 (metallic)", - "Pad 7 (halo)", "Pad 8 (sweep)", "FX 1 (rain)", "FX 2 (soundtrack)", "FX 3 (crystal)", - "FX 4 (atmosphere)", "FX 5 (brightness)", "FX 6 (goblins)", "FX 7 (echoes)", "FX 8 (sci-fi)", - "Sitar", "Banjo", "Shamisen", "Koto", "Kalimba", "Bag pipe", "Fiddle", "Shanai", "Tinkle Bell", - "Agogo", "Steel Drums", "Woodblock", "Taiko Drum", "Melodic Tom", "Synth Drum", "Reverse Cymbal", - "Guitar Fret Noise", "Breath Noise", "Seashore", "Bird Tweet", "Telephone Ring", "Helicopter", - "Applause", "Gunshot" + NEEDS_TRANS("Acoustic Grand Piano"), NEEDS_TRANS("Bright Acoustic Piano"), NEEDS_TRANS("Electric Grand Piano"), NEEDS_TRANS("Honky-tonk Piano"), + NEEDS_TRANS("Electric Piano 1"), NEEDS_TRANS("Electric Piano 2"), NEEDS_TRANS("Harpsichord"), NEEDS_TRANS("Clavinet"), + NEEDS_TRANS("Celesta"), NEEDS_TRANS("Glockenspiel"), NEEDS_TRANS("Music Box"), NEEDS_TRANS("Vibraphone"), + NEEDS_TRANS("Marimba"), NEEDS_TRANS("Xylophone"), NEEDS_TRANS("Tubular Bells"), NEEDS_TRANS("Dulcimer"), + NEEDS_TRANS("Drawbar Organ"), NEEDS_TRANS("Percussive Organ"), NEEDS_TRANS("Rock Organ"), NEEDS_TRANS("Church Organ"), + NEEDS_TRANS("Reed Organ"), NEEDS_TRANS("Accordion"), NEEDS_TRANS("Harmonica"), NEEDS_TRANS("Tango Accordion"), + NEEDS_TRANS("Acoustic Guitar (nylon)"), NEEDS_TRANS("Acoustic Guitar (steel)"), NEEDS_TRANS("Electric Guitar (jazz)"), NEEDS_TRANS("Electric Guitar (clean)"), + NEEDS_TRANS("Electric Guitar (mute)"), NEEDS_TRANS("Overdriven Guitar"), NEEDS_TRANS("Distortion Guitar"), NEEDS_TRANS("Guitar Harmonics"), + NEEDS_TRANS("Acoustic Bass"), NEEDS_TRANS("Electric Bass (finger)"), NEEDS_TRANS("Electric Bass (pick)"), NEEDS_TRANS("Fretless Bass"), + NEEDS_TRANS("Slap Bass 1"), NEEDS_TRANS("Slap Bass 2"), NEEDS_TRANS("Synth Bass 1"), NEEDS_TRANS("Synth Bass 2"), + NEEDS_TRANS("Violin"), NEEDS_TRANS("Viola"), NEEDS_TRANS("Cello"), NEEDS_TRANS("Contrabass"), + NEEDS_TRANS("Tremolo Strings"), NEEDS_TRANS("Pizzicato Strings"), NEEDS_TRANS("Orchestral Harp"), NEEDS_TRANS("Timpani"), + NEEDS_TRANS("String Ensemble 1"), NEEDS_TRANS("String Ensemble 2"), NEEDS_TRANS("SynthStrings 1"), NEEDS_TRANS("SynthStrings 2"), + NEEDS_TRANS("Choir Aahs"), NEEDS_TRANS("Voice Oohs"), NEEDS_TRANS("Synth Voice"), NEEDS_TRANS("Orchestra Hit"), + NEEDS_TRANS("Trumpet"), NEEDS_TRANS("Trombone"), NEEDS_TRANS("Tuba"), NEEDS_TRANS("Muted Trumpet"), + NEEDS_TRANS("French Horn"), NEEDS_TRANS("Brass Section"), NEEDS_TRANS("SynthBrass 1"), NEEDS_TRANS("SynthBrass 2"), + NEEDS_TRANS("Soprano Sax"), NEEDS_TRANS("Alto Sax"), NEEDS_TRANS("Tenor Sax"), NEEDS_TRANS("Baritone Sax"), + NEEDS_TRANS("Oboe"), NEEDS_TRANS("English Horn"), NEEDS_TRANS("Bassoon"), NEEDS_TRANS("Clarinet"), + NEEDS_TRANS("Piccolo"), NEEDS_TRANS("Flute"), NEEDS_TRANS("Recorder"), NEEDS_TRANS("Pan Flute"), + NEEDS_TRANS("Blown Bottle"), NEEDS_TRANS("Shakuhachi"), NEEDS_TRANS("Whistle"), NEEDS_TRANS("Ocarina"), + NEEDS_TRANS("Lead 1 (square)"), NEEDS_TRANS("Lead 2 (sawtooth)"), NEEDS_TRANS("Lead 3 (calliope)"), NEEDS_TRANS("Lead 4 (chiff)"), + NEEDS_TRANS("Lead 5 (charang)"), NEEDS_TRANS("Lead 6 (voice)"), NEEDS_TRANS("Lead 7 (fifths)"), NEEDS_TRANS("Lead 8 (bass+lead)"), + NEEDS_TRANS("Pad 1 (new age)"), NEEDS_TRANS("Pad 2 (warm)"), NEEDS_TRANS("Pad 3 (polysynth)"), NEEDS_TRANS("Pad 4 (choir)"), + NEEDS_TRANS("Pad 5 (bowed)"), NEEDS_TRANS("Pad 6 (metallic)"), NEEDS_TRANS("Pad 7 (halo)"), NEEDS_TRANS("Pad 8 (sweep)"), + NEEDS_TRANS("FX 1 (rain)"), NEEDS_TRANS("FX 2 (soundtrack)"), NEEDS_TRANS("FX 3 (crystal)"), NEEDS_TRANS("FX 4 (atmosphere)"), + NEEDS_TRANS("FX 5 (brightness)"), NEEDS_TRANS("FX 6 (goblins)"), NEEDS_TRANS("FX 7 (echoes)"), NEEDS_TRANS("FX 8 (sci-fi)"), + NEEDS_TRANS("Sitar"), NEEDS_TRANS("Banjo"), NEEDS_TRANS("Shamisen"), NEEDS_TRANS("Koto"), + NEEDS_TRANS("Kalimba"), NEEDS_TRANS("Bag pipe"), NEEDS_TRANS("Fiddle"), NEEDS_TRANS("Shanai"), + NEEDS_TRANS("Tinkle Bell"), NEEDS_TRANS("Agogo"), NEEDS_TRANS("Steel Drums"), NEEDS_TRANS("Woodblock"), + NEEDS_TRANS("Taiko Drum"), NEEDS_TRANS("Melodic Tom"), NEEDS_TRANS("Synth Drum"), NEEDS_TRANS("Reverse Cymbal"), + NEEDS_TRANS("Guitar Fret Noise"), NEEDS_TRANS("Breath Noise"), NEEDS_TRANS("Seashore"), NEEDS_TRANS("Bird Tweet"), + NEEDS_TRANS("Telephone Ring"), NEEDS_TRANS("Helicopter"), NEEDS_TRANS("Applause"), NEEDS_TRANS("Gunshot") }; return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr; @@ -999,10 +986,10 @@ const char* MidiMessage::getGMInstrumentBankName (const int n) { static const char* names[] = { - "Piano", "Chromatic Percussion", "Organ", "Guitar", - "Bass", "Strings", "Ensemble", "Brass", - "Reed", "Pipe", "Synth Lead", "Synth Pad", - "Synth Effects", "Ethnic", "Percussive", "Sound Effects" + NEEDS_TRANS("Piano"), NEEDS_TRANS("Chromatic Percussion"), NEEDS_TRANS("Organ"), NEEDS_TRANS("Guitar"), + NEEDS_TRANS("Bass"), NEEDS_TRANS("Strings"), NEEDS_TRANS("Ensemble"), NEEDS_TRANS("Brass"), + NEEDS_TRANS("Reed"), NEEDS_TRANS("Pipe"), NEEDS_TRANS("Synth Lead"), NEEDS_TRANS("Synth Pad"), + NEEDS_TRANS("Synth Effects"), NEEDS_TRANS("Ethnic"), NEEDS_TRANS("Percussive"), NEEDS_TRANS("Sound Effects") }; return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr; @@ -1012,15 +999,18 @@ const char* MidiMessage::getRhythmInstrumentName (const int n) { static const char* names[] = { - "Acoustic Bass Drum", "Bass Drum 1", "Side Stick", "Acoustic Snare", - "Hand Clap", "Electric Snare", "Low Floor Tom", "Closed Hi-Hat", "High Floor Tom", - "Pedal Hi-Hat", "Low Tom", "Open Hi-Hat", "Low-Mid Tom", "Hi-Mid Tom", "Crash Cymbal 1", - "High Tom", "Ride Cymbal 1", "Chinese Cymbal", "Ride Bell", "Tambourine", "Splash Cymbal", - "Cowbell", "Crash Cymbal 2", "Vibraslap", "Ride Cymbal 2", "Hi Bongo", "Low Bongo", - "Mute Hi Conga", "Open Hi Conga", "Low Conga", "High Timbale", "Low Timbale", "High Agogo", - "Low Agogo", "Cabasa", "Maracas", "Short Whistle", "Long Whistle", "Short Guiro", - "Long Guiro", "Claves", "Hi Wood Block", "Low Wood Block", "Mute Cuica", "Open Cuica", - "Mute Triangle", "Open Triangle" + NEEDS_TRANS("Acoustic Bass Drum"), NEEDS_TRANS("Bass Drum 1"), NEEDS_TRANS("Side Stick"), NEEDS_TRANS("Acoustic Snare"), + NEEDS_TRANS("Hand Clap"), NEEDS_TRANS("Electric Snare"), NEEDS_TRANS("Low Floor Tom"), NEEDS_TRANS("Closed Hi-Hat"), + NEEDS_TRANS("High Floor Tom"), NEEDS_TRANS("Pedal Hi-Hat"), NEEDS_TRANS("Low Tom"), NEEDS_TRANS("Open Hi-Hat"), + NEEDS_TRANS("Low-Mid Tom"), NEEDS_TRANS("Hi-Mid Tom"), NEEDS_TRANS("Crash Cymbal 1"), NEEDS_TRANS("High Tom"), + NEEDS_TRANS("Ride Cymbal 1"), NEEDS_TRANS("Chinese Cymbal"), NEEDS_TRANS("Ride Bell"), NEEDS_TRANS("Tambourine"), + NEEDS_TRANS("Splash Cymbal"), NEEDS_TRANS("Cowbell"), NEEDS_TRANS("Crash Cymbal 2"), NEEDS_TRANS("Vibraslap"), + NEEDS_TRANS("Ride Cymbal 2"), NEEDS_TRANS("Hi Bongo"), NEEDS_TRANS("Low Bongo"), NEEDS_TRANS("Mute Hi Conga"), + NEEDS_TRANS("Open Hi Conga"), NEEDS_TRANS("Low Conga"), NEEDS_TRANS("High Timbale"), NEEDS_TRANS("Low Timbale"), + NEEDS_TRANS("High Agogo"), NEEDS_TRANS("Low Agogo"), NEEDS_TRANS("Cabasa"), NEEDS_TRANS("Maracas"), + NEEDS_TRANS("Short Whistle"), NEEDS_TRANS("Long Whistle"), NEEDS_TRANS("Short Guiro"), NEEDS_TRANS("Long Guiro"), + NEEDS_TRANS("Claves"), NEEDS_TRANS("Hi Wood Block"), NEEDS_TRANS("Low Wood Block"), NEEDS_TRANS("Mute Cuica"), + NEEDS_TRANS("Open Cuica"), NEEDS_TRANS("Mute Triangle"), NEEDS_TRANS("Open Triangle") }; return (n >= 35 && n <= 81) ? names [n - 35] : nullptr; @@ -1030,28 +1020,38 @@ const char* MidiMessage::getControllerName (const int n) { static const char* names[] = { - "Bank Select", "Modulation Wheel (coarse)", "Breath controller (coarse)", - 0, "Foot Pedal (coarse)", "Portamento Time (coarse)", - "Data Entry (coarse)", "Volume (coarse)", "Balance (coarse)", - 0, "Pan position (coarse)", "Expression (coarse)", "Effect Control 1 (coarse)", - "Effect Control 2 (coarse)", 0, 0, "General Purpose Slider 1", "General Purpose Slider 2", - "General Purpose Slider 3", "General Purpose Slider 4", 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, "Bank Select (fine)", "Modulation Wheel (fine)", "Breath controller (fine)", - 0, "Foot Pedal (fine)", "Portamento Time (fine)", "Data Entry (fine)", "Volume (fine)", - "Balance (fine)", 0, "Pan position (fine)", "Expression (fine)", "Effect Control 1 (fine)", - "Effect Control 2 (fine)", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - "Hold Pedal (on/off)", "Portamento (on/off)", "Sustenuto Pedal (on/off)", "Soft Pedal (on/off)", - "Legato Pedal (on/off)", "Hold 2 Pedal (on/off)", "Sound Variation", "Sound Timbre", - "Sound Release Time", "Sound Attack Time", "Sound Brightness", "Sound Control 6", - "Sound Control 7", "Sound Control 8", "Sound Control 9", "Sound Control 10", - "General Purpose Button 1 (on/off)", "General Purpose Button 2 (on/off)", - "General Purpose Button 3 (on/off)", "General Purpose Button 4 (on/off)", - 0, 0, 0, 0, 0, 0, 0, "Reverb Level", "Tremolo Level", "Chorus Level", "Celeste Level", - "Phaser Level", "Data Button increment", "Data Button decrement", "Non-registered Parameter (fine)", - "Non-registered Parameter (coarse)", "Registered Parameter (fine)", "Registered Parameter (coarse)", - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "All Sound Off", "All Controllers Off", - "Local Keyboard (on/off)", "All Notes Off", "Omni Mode Off", "Omni Mode On", "Mono Operation", - "Poly Operation" + NEEDS_TRANS("Bank Select"), NEEDS_TRANS("Modulation Wheel (coarse)"), NEEDS_TRANS("Breath controller (coarse)"), + nullptr, + NEEDS_TRANS("Foot Pedal (coarse)"), NEEDS_TRANS("Portamento Time (coarse)"), NEEDS_TRANS("Data Entry (coarse)"), + NEEDS_TRANS("Volume (coarse)"), NEEDS_TRANS("Balance (coarse)"), + nullptr, + NEEDS_TRANS("Pan position (coarse)"), NEEDS_TRANS("Expression (coarse)"), NEEDS_TRANS("Effect Control 1 (coarse)"), + NEEDS_TRANS("Effect Control 2 (coarse)"), + nullptr, nullptr, + NEEDS_TRANS("General Purpose Slider 1"), NEEDS_TRANS("General Purpose Slider 2"), + NEEDS_TRANS("General Purpose Slider 3"), NEEDS_TRANS("General Purpose Slider 4"), + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + NEEDS_TRANS("Bank Select (fine)"), NEEDS_TRANS("Modulation Wheel (fine)"), NEEDS_TRANS("Breath controller (fine)"), + nullptr, + NEEDS_TRANS("Foot Pedal (fine)"), NEEDS_TRANS("Portamento Time (fine)"), NEEDS_TRANS("Data Entry (fine)"), NEEDS_TRANS("Volume (fine)"), + NEEDS_TRANS("Balance (fine)"), nullptr, NEEDS_TRANS("Pan position (fine)"), NEEDS_TRANS("Expression (fine)"), + NEEDS_TRANS("Effect Control 1 (fine)"), NEEDS_TRANS("Effect Control 2 (fine)"), + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + NEEDS_TRANS("Hold Pedal (on/off)"), NEEDS_TRANS("Portamento (on/off)"), NEEDS_TRANS("Sustenuto Pedal (on/off)"), NEEDS_TRANS("Soft Pedal (on/off)"), + NEEDS_TRANS("Legato Pedal (on/off)"), NEEDS_TRANS("Hold 2 Pedal (on/off)"), NEEDS_TRANS("Sound Variation"), NEEDS_TRANS("Sound Timbre"), + NEEDS_TRANS("Sound Release Time"), NEEDS_TRANS("Sound Attack Time"), NEEDS_TRANS("Sound Brightness"), NEEDS_TRANS("Sound Control 6"), + NEEDS_TRANS("Sound Control 7"), NEEDS_TRANS("Sound Control 8"), NEEDS_TRANS("Sound Control 9"), NEEDS_TRANS("Sound Control 10"), + NEEDS_TRANS("General Purpose Button 1 (on/off)"), NEEDS_TRANS("General Purpose Button 2 (on/off)"), + NEEDS_TRANS("General Purpose Button 3 (on/off)"), NEEDS_TRANS("General Purpose Button 4 (on/off)"), + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + NEEDS_TRANS("Reverb Level"), NEEDS_TRANS("Tremolo Level"), NEEDS_TRANS("Chorus Level"), NEEDS_TRANS("Celeste Level"), + NEEDS_TRANS("Phaser Level"), NEEDS_TRANS("Data Button increment"), NEEDS_TRANS("Data Button decrement"), NEEDS_TRANS("Non-registered Parameter (fine)"), + NEEDS_TRANS("Non-registered Parameter (coarse)"), NEEDS_TRANS("Registered Parameter (fine)"), NEEDS_TRANS("Registered Parameter (coarse)"), + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + NEEDS_TRANS("All Sound Off"), NEEDS_TRANS("All Controllers Off"), NEEDS_TRANS("Local Keyboard (on/off)"), NEEDS_TRANS("All Notes Off"), + NEEDS_TRANS("Omni Mode Off"), NEEDS_TRANS("Omni Mode On"), NEEDS_TRANS("Mono Operation"), NEEDS_TRANS("Poly Operation") }; return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr; diff --git a/source/modules/juce_audio_basics/midi/juce_MidiMessage.h b/source/modules/juce_audio_basics/midi/juce_MidiMessage.h index fe4ee259e..c775883d3 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiMessage.h +++ b/source/modules/juce_audio_basics/midi/juce_MidiMessage.h @@ -114,12 +114,12 @@ public: /** Returns a pointer to the raw midi data. @see getRawDataSize */ - const uint8* getRawData() const noexcept { return data; } + const uint8* getRawData() const noexcept { return allocatedData != nullptr ? allocatedData.getData() : preallocatedData.asBytes; } /** Returns the number of bytes of data in the message. @see getRawData */ - int getRawDataSize() const noexcept { return size; } + int getRawDataSize() const noexcept { return size; } //============================================================================== /** Returns the timestamp associated with this message. @@ -138,18 +138,18 @@ public: @see setTimeStamp, addToTimeStamp */ - double getTimeStamp() const noexcept { return timeStamp; } + double getTimeStamp() const noexcept { return timeStamp; } /** Changes the message's associated timestamp. The units for the timestamp will be application-specific - see the notes for getTimeStamp(). @see addToTimeStamp, getTimeStamp */ - void setTimeStamp (double newTimestamp) noexcept { timeStamp = newTimestamp; } + void setTimeStamp (double newTimestamp) noexcept { timeStamp = newTimestamp; } /** Adds a value to the message's timestamp. The units for the timestamp will be application-specific. */ - void addToTimeStamp (double delta) noexcept { timeStamp += delta; } + void addToTimeStamp (double delta) noexcept { timeStamp += delta; } //============================================================================== /** Returns the midi channel associated with the message. @@ -914,7 +914,7 @@ public: private: //============================================================================== double timeStamp; - uint8* data; + HeapBlock allocatedData; int size; #ifndef DOXYGEN @@ -925,9 +925,8 @@ private: } preallocatedData; #endif - void freeData() noexcept; - void setToUseInternalData() noexcept; - bool usesAllocatedData() const noexcept; + inline uint8* getData() noexcept { return allocatedData != nullptr ? allocatedData.getData() : preallocatedData.asBytes; } + uint8* allocateSpace (int); }; #endif // JUCE_MIDIMESSAGE_H_INCLUDED diff --git a/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp b/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp index ded0dce4f..effefbbb4 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp +++ b/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp @@ -28,10 +28,7 @@ MidiMessageSequence::MidiMessageSequence() MidiMessageSequence::MidiMessageSequence (const MidiMessageSequence& other) { - list.ensureStorageAllocated (other.list.size()); - - for (int i = 0; i < other.list.size(); ++i) - list.add (new MidiEventHolder (other.list.getUnchecked(i)->message)); + list.addCopiesOf (other.list); } MidiMessageSequence& MidiMessageSequence::operator= (const MidiMessageSequence& other) diff --git a/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h b/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h index c44e448be..d91ed8b24 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h +++ b/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h @@ -43,10 +43,10 @@ public: MidiMessageSequence(); /** Creates a copy of another sequence. */ - MidiMessageSequence (const MidiMessageSequence& other); + MidiMessageSequence (const MidiMessageSequence&); /** Replaces this sequence with another one. */ - MidiMessageSequence& operator= (const MidiMessageSequence& other); + MidiMessageSequence& operator= (const MidiMessageSequence&); /** Destructor. */ ~MidiMessageSequence(); @@ -66,13 +66,12 @@ public: /** Destructor. */ ~MidiEventHolder(); - /** The message itself, whose timestamp is used to specify the event's time. - */ + /** The message itself, whose timestamp is used to specify the event's time. */ MidiMessage message; /** The matching note-off event (if this is a note-on event). - If this isn't a note-on, this pointer will be null. + If this isn't a note-on, this pointer will be nullptr. Use the MidiMessageSequence::updateMatchedPairs() method to keep these note-offs up-to-date after events have been moved around in the sequence @@ -83,7 +82,7 @@ public: private: //============================================================================== friend class MidiMessageSequence; - MidiEventHolder (const MidiMessage& message); + MidiEventHolder (const MidiMessage&); JUCE_LEAK_DETECTOR (MidiEventHolder) }; @@ -98,17 +97,13 @@ public: MidiEventHolder* getEventPointer (int index) const; /** Returns the time of the note-up that matches the note-on at this index. - If the event at this index isn't a note-on, it'll just return 0. - @see MidiMessageSequence::MidiEventHolder::noteOffObject */ double getTimeOfMatchingKeyUp (int index) const; /** Returns the index of the note-up that matches the note-on at this index. - If the event at this index isn't a note-on, it'll just return -1. - @see MidiMessageSequence::MidiEventHolder::noteOffObject */ int getIndexOfMatchingKeyUp (int index) const; @@ -117,7 +112,6 @@ public: int getIndexOf (MidiEventHolder* event) const; /** Returns the index of the first event on or after the given timestamp. - If the time is beyond the end of the sequence, this will return the number of events. */ @@ -125,19 +119,16 @@ public: //============================================================================== /** Returns the timestamp of the first event in the sequence. - @see getEndTime */ double getStartTime() const; /** Returns the timestamp of the last event in the sequence. - @see getStartTime */ double getEndTime() const; /** Returns the timestamp of the event at a given index. - If the index is out-of-range, this will return 0.0 */ double getEventTime (int index) const; @@ -190,7 +181,7 @@ public: //============================================================================== /** Makes sure all the note-on and note-off pairs are up-to-date. - Call this after moving messages about or deleting/adding messages, and it + Call this after re-ordering messages or deleting/adding messages, and it will scan the list and make sure all the note-offs in the MidiEventHolder structures are pointing at the correct ones. */ @@ -216,7 +207,6 @@ public: bool alsoIncludeMetaEvents) const; /** Copies all midi sys-ex messages to another sequence. - @param destSequence this is the sequence to which any sys-exes in this sequence will be added @see extractMidiChannelMessages @@ -224,17 +214,14 @@ public: void extractSysExMessages (MidiMessageSequence& destSequence) const; /** Removes any messages in this sequence that have a specific midi channel. - @param channelNumberToRemove the midi channel to look for, in the range 1 to 16 */ void deleteMidiChannelMessages (int channelNumberToRemove); - /** Removes any sys-ex messages from this sequence. - */ + /** Removes any sys-ex messages from this sequence. */ void deleteSysExMessages(); /** Adds an offset to the timestamps of all events in the sequence. - @param deltaTime the amount to add to each timestamp. */ void addTimeToMessages (double deltaTime); @@ -264,12 +251,12 @@ public: //============================================================================== /** Swaps this sequence with another one. */ - void swapWith (MidiMessageSequence& other) noexcept; + void swapWith (MidiMessageSequence&) noexcept; private: //============================================================================== friend class MidiFile; - OwnedArray list; + OwnedArray list; JUCE_LEAK_DETECTOR (MidiMessageSequence) }; diff --git a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp index 6af687ede..1aa248bb0 100644 --- a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp +++ b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp @@ -56,6 +56,8 @@ void SynthesiserVoice::clearCurrentNote() currentlyPlayingSound = nullptr; } +void SynthesiserVoice::aftertouchChanged (int) {} + //============================================================================== Synthesiser::Synthesiser() : sampleRate (0), @@ -191,6 +193,10 @@ void Synthesiser::handleMidiEvent (const MidiMessage& m) handlePitchWheel (channel, wheelPos); } + else if (m.isAftertouch()) + { + handleAftertouch (m.getChannel(), m.getNoteNumber(), m.getAfterTouchValue()); + } else if (m.isController()) { handleController (m.getChannel(), m.getControllerNumber(), m.getControllerValue()); @@ -338,6 +344,20 @@ void Synthesiser::handleController (const int midiChannel, } } +void Synthesiser::handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue) +{ + const ScopedLock sl (lock); + + for (int i = voices.size(); --i >= 0;) + { + SynthesiserVoice* const voice = voices.getUnchecked (i); + + if (voice->getCurrentlyPlayingNote() == midiNoteNumber + && (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))) + voice->aftertouchChanged (aftertouchValue); + } +} + void Synthesiser::handleSustainPedal (int midiChannel, bool isDown) { jassert (midiChannel > 0 && midiChannel <= 16); diff --git a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h index eb443cfd8..7202b4b28 100644 --- a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h +++ b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h @@ -116,7 +116,6 @@ public: virtual bool canPlaySound (SynthesiserSound*) = 0; /** Called to start a new note. - This will be called during the rendering callback, so must be fast and thread-safe. */ virtual void startNote (int midiNoteNumber, @@ -142,12 +141,17 @@ public: /** 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. */ - virtual void pitchWheelMoved (int newValue) = 0; + virtual void pitchWheelMoved (int newPitchWheelValue) = 0; /** Called to let the voice know that a midi controller has been moved. This will be called during the rendering callback, so must be fast and thread-safe. */ - virtual void controllerMoved (int controllerNumber, int newValue) = 0; + virtual void controllerMoved (int controllerNumber, int newControllerValue) = 0; + + /** Called to let the voice know that the aftertouch has changed. + This will be called during the rendering callback, so must be fast and thread-safe. + */ + virtual void aftertouchChanged (int newAftertouchValue); //============================================================================== /** Renders the next block of data for this voice. @@ -186,6 +190,14 @@ public: */ void setCurrentPlaybackSampleRate (double newRate); + /** 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 + sostenuto pedal is down). + */ + bool isKeyDown() const noexcept { return keyIsDown; } + + /** Returns true if the sostenuto pedal is currently active for this voice. */ + bool isSostenutoPedalDown() const noexcept { return sostenutoPedalDown; } protected: //============================================================================== @@ -218,8 +230,7 @@ private: int currentlyPlayingNote; uint32 noteOnTime; SynthesiserSound::Ptr currentlyPlayingSound; - bool keyIsDown; // the voice may still be playing when the key is not down (i.e. sustain pedal) - bool sostenutoPedalDown; + bool keyIsDown, sostenutoPedalDown; JUCE_LEAK_DETECTOR (SynthesiserVoice) }; @@ -400,6 +411,21 @@ public: int controllerNumber, int controllerValue); + /** Sends an aftertouch message. + + This will send an aftertouch message to any voices that are playing sounds on + the given midi channel and note number. + + This method will be called automatically according to the midi data passed into + renderNextBlock(), but may be called explicitly too. + + @param midiChannel the midi channel, from 1 to 16 inclusive + @param midiNoteNumber the midi note number, 0 to 127 + @param aftertouchValue the aftertouch value, between 0 and 127, + as returned by MidiMessage::getAftertouchValue() + */ + virtual void handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue); + /** Handles a sustain pedal event. */ virtual void handleSustainPedal (int midiChannel, bool isDown);