@@ -38,8 +38,10 @@ _old | |||
ports/_todo | |||
libs/juce/source/.gitignore | |||
libs/juce/source/ChangeList.txt | |||
libs/juce/source/amalgamation/ | |||
libs/juce/source/docs/ | |||
libs/juce/source/examples/ | |||
libs/juce/source/extras/ | |||
libs/juce/source/juce.h | |||
libs/juce/source/juce_amalgamated.cpp | |||
@@ -47,7 +49,5 @@ libs/juce/source/juce_amalgamated.h | |||
libs/juce/source/juce_amalgamated.mm | |||
libs/juce/source/modules/juce_box2d/ | |||
libs/juce/source/modules/juce_browser_plugin_client/ | |||
libs/juce/source/modules/juce_cryptography/ | |||
libs/juce/source/modules/juce_opengl/ | |||
libs/juce/source/modules/juce_tracktion_marketplace/ | |||
libs/juce/source/modules/juce_video/ |
@@ -1,16 +0,0 @@ | |||
diff --git a/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp b/libs/juce/source/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp | |||
index b88a3b2..d057fb0 100644 | |||
--- a/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp | |||
+++ b/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp | |||
@@ -1180,8 +1180,10 @@ public: | |||
editorComp->addToDesktop (0, ptr); | |||
hostWindow = (HWND) ptr; | |||
#elif JUCE_LINUX | |||
- editorComp->addToDesktop (0, ptr); | |||
+ editorComp->addToDesktop (0); | |||
hostWindow = (Window) ptr; | |||
+ Window editorWnd = (Window) editorComp->getWindowHandle(); | |||
+ XReparentWindow (display, editorWnd, hostWindow, 0, 0); | |||
#else | |||
hostWindow = attachComponentToWindowRef (editorComp, ptr, useNSView); | |||
#endif |
@@ -0,0 +1,12 @@ | |||
--- a/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp | |||
+++ b/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp | |||
@@ -1403,7 +1403,8 @@ class VST3ModuleHandle : public ReferenceCountedObject | |||
const Result result (finder.findDescriptionsAndPerform (f)); | |||
- if (result.getErrorMessage() == MatchingDescriptionFinder::getSuccessString()) | |||
+ if (result.getErrorMessage() == MatchingDescriptionFinder::getSuccessString() || | |||
+ result.getErrorMessage().isEmpty()) | |||
{ | |||
name = description.name; | |||
return true; |
@@ -7,5 +7,11 @@ It contains pretty much everything you're likely to need to create | |||
most applications, and is particularly well-suited for building | |||
highly-customised GUIs, and for handling graphics and sound. | |||
Most JUCE modules are shared under the GNU Public Licence | |||
(GPLv2, v3, and the AGPLv3). This means that the code can | |||
be freely copied and distributed, and costs nothing to use | |||
in other GPL applications. One module (the juce_core module) | |||
is permissively licensed under the ISC. | |||
For more information, visit the website: | |||
http://www.juce.com |
@@ -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<float> findMinAndMax (size_t numSamples) const noexcept | |||
{ | |||
if (numSamples == 0) | |||
{ | |||
minValue = maxValue = 0; | |||
return; | |||
} | |||
return Range<float>(); | |||
Pointer dest (*this); | |||
@@ -512,27 +509,32 @@ public: | |||
if (v < mn) mn = v; | |||
} | |||
minValue = mn; | |||
maxValue = mx; | |||
return Range<float> (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<float> (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<float> r (findMinAndMax (numSamples)); | |||
minValue = r.getStart(); | |||
maxValue = r.getEnd(); | |||
} | |||
/** Returns true if the pointer is using a floating-point format. */ | |||
@@ -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<int typeSize> 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<float> 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) | |||
@@ -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<float> JUCE_CALLTYPE findMinAndMax (const float* src, int numValues) noexcept; | |||
@@ -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<Complex> 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<Complex*> (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<Complex*> (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<Complex*> (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<Complex*> (alloca (16 + sizeof (Complex) * (size_t) size)); | |||
perform (reinterpret_cast<const Complex*> (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; | |||
} | |||
} | |||
} |
@@ -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<FFTConfig> config; | |||
const int size; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FFT) | |||
}; |
@@ -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); | |||
@@ -1,239 +0,0 @@ | |||
/* | |||
============================================================================== | |||
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. | |||
============================================================================== | |||
*/ | |||
#if JUCE_INTEL | |||
#define JUCE_SNAP_TO_ZERO(n) if (! (n < -1.0e-8 || n > 1.0e-8)) n = 0; | |||
#else | |||
#define JUCE_SNAP_TO_ZERO(n) | |||
#endif | |||
//============================================================================== | |||
IIRFilterOld::IIRFilterOld() | |||
: active (false), v1 (0), v2 (0) | |||
{ | |||
zeromem (coefficients, sizeof (coefficients)); | |||
} | |||
IIRFilterOld::IIRFilterOld (const IIRFilterOld& other) | |||
: active (other.active), v1 (0), v2 (0) | |||
{ | |||
const SpinLock::ScopedLockType sl (other.processLock); | |||
memcpy (coefficients, other.coefficients, sizeof (coefficients)); | |||
} | |||
IIRFilterOld::~IIRFilterOld() | |||
{ | |||
} | |||
//============================================================================== | |||
void IIRFilterOld::reset() noexcept | |||
{ | |||
const SpinLock::ScopedLockType sl (processLock); | |||
v1 = v2 = 0; | |||
} | |||
float IIRFilterOld::processSingleSampleRaw (const float in) noexcept | |||
{ | |||
float out = coefficients[0] * in + v1; | |||
JUCE_SNAP_TO_ZERO (out); | |||
v1 = coefficients[1] * in - coefficients[3] * out + v2; | |||
v2 = coefficients[2] * in - coefficients[4] * out; | |||
return out; | |||
} | |||
void IIRFilterOld::processSamples (float* const samples, | |||
const int numSamples) noexcept | |||
{ | |||
const SpinLock::ScopedLockType sl (processLock); | |||
if (active) | |||
{ | |||
const float c0 = coefficients[0]; | |||
const float c1 = coefficients[1]; | |||
const float c2 = coefficients[2]; | |||
const float c3 = coefficients[3]; | |||
const float c4 = coefficients[4]; | |||
float lv1 = v1, lv2 = v2; | |||
for (int i = 0; i < numSamples; ++i) | |||
{ | |||
const float in = samples[i]; | |||
const float out = c0 * in + lv1; | |||
samples[i] = out; | |||
lv1 = c1 * in - c3 * out + lv2; | |||
lv2 = c2 * in - c4 * out; | |||
} | |||
JUCE_SNAP_TO_ZERO (lv1); v1 = lv1; | |||
JUCE_SNAP_TO_ZERO (lv2); v2 = lv2; | |||
} | |||
} | |||
//============================================================================== | |||
void IIRFilterOld::makeLowPass (const double sampleRate, | |||
const double frequency) noexcept | |||
{ | |||
jassert (sampleRate > 0); | |||
const double n = 1.0 / tan (double_Pi * frequency / sampleRate); | |||
const double nSquared = n * n; | |||
const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared); | |||
setCoefficients (c1, | |||
c1 * 2.0f, | |||
c1, | |||
1.0, | |||
c1 * 2.0 * (1.0 - nSquared), | |||
c1 * (1.0 - std::sqrt (2.0) * n + nSquared)); | |||
} | |||
void IIRFilterOld::makeHighPass (const double sampleRate, | |||
const double frequency) noexcept | |||
{ | |||
const double n = tan (double_Pi * frequency / sampleRate); | |||
const double nSquared = n * n; | |||
const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared); | |||
setCoefficients (c1, | |||
c1 * -2.0f, | |||
c1, | |||
1.0, | |||
c1 * 2.0 * (nSquared - 1.0), | |||
c1 * (1.0 - std::sqrt (2.0) * n + nSquared)); | |||
} | |||
void IIRFilterOld::makeLowShelf (const double sampleRate, | |||
const double cutOffFrequency, | |||
const double Q, | |||
const float gainFactor) noexcept | |||
{ | |||
jassert (sampleRate > 0); | |||
jassert (Q > 0); | |||
const double A = jmax (0.0f, gainFactor); | |||
const double aminus1 = A - 1.0; | |||
const double aplus1 = A + 1.0; | |||
const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate; | |||
const double coso = std::cos (omega); | |||
const double beta = std::sin (omega) * std::sqrt (A) / Q; | |||
const double aminus1TimesCoso = aminus1 * coso; | |||
setCoefficients (A * (aplus1 - aminus1TimesCoso + beta), | |||
A * 2.0 * (aminus1 - aplus1 * coso), | |||
A * (aplus1 - aminus1TimesCoso - beta), | |||
aplus1 + aminus1TimesCoso + beta, | |||
-2.0 * (aminus1 + aplus1 * coso), | |||
aplus1 + aminus1TimesCoso - beta); | |||
} | |||
void IIRFilterOld::makeHighShelf (const double sampleRate, | |||
const double cutOffFrequency, | |||
const double Q, | |||
const float gainFactor) noexcept | |||
{ | |||
jassert (sampleRate > 0); | |||
jassert (Q > 0); | |||
const double A = jmax (0.0f, gainFactor); | |||
const double aminus1 = A - 1.0; | |||
const double aplus1 = A + 1.0; | |||
const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate; | |||
const double coso = std::cos (omega); | |||
const double beta = std::sin (omega) * std::sqrt (A) / Q; | |||
const double aminus1TimesCoso = aminus1 * coso; | |||
setCoefficients (A * (aplus1 + aminus1TimesCoso + beta), | |||
A * -2.0 * (aminus1 + aplus1 * coso), | |||
A * (aplus1 + aminus1TimesCoso - beta), | |||
aplus1 - aminus1TimesCoso + beta, | |||
2.0 * (aminus1 - aplus1 * coso), | |||
aplus1 - aminus1TimesCoso - beta); | |||
} | |||
void IIRFilterOld::makeBandPass (const double sampleRate, | |||
const double centreFrequency, | |||
const double Q, | |||
const float gainFactor) noexcept | |||
{ | |||
jassert (sampleRate > 0); | |||
jassert (Q > 0); | |||
const double A = jmax (0.0f, gainFactor); | |||
const double omega = (double_Pi * 2.0 * jmax (centreFrequency, 2.0)) / sampleRate; | |||
const double alpha = 0.5 * std::sin (omega) / Q; | |||
const double c2 = -2.0 * std::cos (omega); | |||
const double alphaTimesA = alpha * A; | |||
const double alphaOverA = alpha / A; | |||
setCoefficients (1.0 + alphaTimesA, | |||
c2, | |||
1.0 - alphaTimesA, | |||
1.0 + alphaOverA, | |||
c2, | |||
1.0 - alphaOverA); | |||
} | |||
void IIRFilterOld::makeInactive() noexcept | |||
{ | |||
const SpinLock::ScopedLockType sl (processLock); | |||
active = false; | |||
} | |||
//============================================================================== | |||
void IIRFilterOld::copyCoefficientsFrom (const IIRFilterOld& other) noexcept | |||
{ | |||
const SpinLock::ScopedLockType sl (processLock); | |||
memcpy (coefficients, other.coefficients, sizeof (coefficients)); | |||
active = other.active; | |||
} | |||
//============================================================================== | |||
void IIRFilterOld::setCoefficients (double c1, double c2, double c3, | |||
double c4, double c5, double c6) noexcept | |||
{ | |||
const double a = 1.0 / c4; | |||
c1 *= a; | |||
c2 *= a; | |||
c3 *= a; | |||
c5 *= a; | |||
c6 *= a; | |||
const SpinLock::ScopedLockType sl (processLock); | |||
coefficients[0] = (float) c1; | |||
coefficients[1] = (float) c2; | |||
coefficients[2] = (float) c3; | |||
coefficients[3] = (float) c5; | |||
coefficients[4] = (float) c6; | |||
active = true; | |||
} | |||
#undef JUCE_SNAP_TO_ZERO |
@@ -1,148 +0,0 @@ | |||
/* | |||
============================================================================== | |||
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_IIRFILTER_OLD_JUCEHEADER__ | |||
#define __JUCE_IIRFILTER_OLD_JUCEHEADER__ | |||
//============================================================================== | |||
/** | |||
An IIR filter that can perform low, high, or band-pass filtering on an | |||
audio signal. | |||
@see IIRFilterAudioSource | |||
*/ | |||
class JUCE_API IIRFilterOld | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates a filter. | |||
Initially the filter is inactive, so will have no effect on samples that | |||
you process with it. Use the appropriate method to turn it into the type | |||
of filter needed. | |||
*/ | |||
IIRFilterOld(); | |||
/** Creates a copy of another filter. */ | |||
IIRFilterOld (const IIRFilterOld& other); | |||
/** Destructor. */ | |||
~IIRFilterOld(); | |||
//============================================================================== | |||
/** Resets the filter's processing pipeline, ready to start a new stream of data. | |||
Note that this clears the processing state, but the type of filter and | |||
its coefficients aren't changed. To put a filter into an inactive state, use | |||
the makeInactive() method. | |||
*/ | |||
void reset() noexcept; | |||
/** Performs the filter operation on the given set of samples. | |||
*/ | |||
void processSamples (float* samples, | |||
int numSamples) noexcept; | |||
/** Processes a single sample, without any locking or checking. | |||
Use this if you need fast processing of a single value, but be aware that | |||
this isn't thread-safe in the way that processSamples() is. | |||
*/ | |||
float processSingleSampleRaw (float sample) noexcept; | |||
//============================================================================== | |||
/** Sets the filter up to act as a low-pass filter. | |||
*/ | |||
void makeLowPass (double sampleRate, | |||
double frequency) noexcept; | |||
/** Sets the filter up to act as a high-pass filter. | |||
*/ | |||
void makeHighPass (double sampleRate, | |||
double frequency) noexcept; | |||
//============================================================================== | |||
/** Sets the filter up to act as a low-pass shelf filter with variable Q and gain. | |||
The gain is a scale factor that the low frequencies are multiplied by, so values | |||
greater than 1.0 will boost the low frequencies, values less than 1.0 will | |||
attenuate them. | |||
*/ | |||
void makeLowShelf (double sampleRate, | |||
double cutOffFrequency, | |||
double Q, | |||
float gainFactor) noexcept; | |||
/** Sets the filter up to act as a high-pass shelf filter with variable Q and gain. | |||
The gain is a scale factor that the high frequencies are multiplied by, so values | |||
greater than 1.0 will boost the high frequencies, values less than 1.0 will | |||
attenuate them. | |||
*/ | |||
void makeHighShelf (double sampleRate, | |||
double cutOffFrequency, | |||
double Q, | |||
float gainFactor) noexcept; | |||
/** Sets the filter up to act as a band pass filter centred around a | |||
frequency, with a variable Q and gain. | |||
The gain is a scale factor that the centre frequencies are multiplied by, so | |||
values greater than 1.0 will boost the centre frequencies, values less than | |||
1.0 will attenuate them. | |||
*/ | |||
void makeBandPass (double sampleRate, | |||
double centreFrequency, | |||
double Q, | |||
float gainFactor) noexcept; | |||
/** Clears the filter's coefficients so that it becomes inactive. | |||
*/ | |||
void makeInactive() noexcept; | |||
//============================================================================== | |||
/** Makes this filter duplicate the set-up of another one. | |||
*/ | |||
void copyCoefficientsFrom (const IIRFilterOld& other) noexcept; | |||
protected: | |||
//============================================================================== | |||
SpinLock processLock; | |||
void setCoefficients (double c1, double c2, double c3, | |||
double c4, double c5, double c6) noexcept; | |||
bool active; | |||
float coefficients[5]; | |||
float v1, v2; | |||
// (use the copyCoefficientsFrom() method instead of this operator) | |||
IIRFilterOld& operator= (const IIRFilterOld&); | |||
JUCE_LEAK_DETECTOR (IIRFilterOld) | |||
}; | |||
#endif // __JUCE_IIRFILTER_OLD_JUCEHEADER__ |
@@ -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 | |||
{ | |||
@@ -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<float> 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) | |||
}; | |||
@@ -76,8 +76,8 @@ namespace juce | |||
#include "buffers/juce_AudioSampleBuffer.cpp" | |||
#include "buffers/juce_FloatVectorOperations.cpp" | |||
#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" | |||
@@ -31,13 +31,16 @@ | |||
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" | |||
#include "effects/juce_Decibels.h" | |||
#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" | |||
@@ -1,7 +1,7 @@ | |||
{ | |||
"id": "juce_audio_basics", | |||
"name": "JUCE audio and midi data classes", | |||
"version": "3.0.8", | |||
"version": "3.1.1", | |||
"description": "Classes for audio buffer manipulation, midi message handling, synthesis, etc", | |||
"website": "http://www.juce.com/juce", | |||
"license": "GPL/Commercial", | |||
@@ -276,14 +276,11 @@ void MidiMessageSequence::deleteSysExMessages() | |||
} | |||
//============================================================================== | |||
void MidiMessageSequence::createControllerUpdatesForTime (const int channelNumber, | |||
const double time, | |||
OwnedArray<MidiMessage>& dest) | |||
void MidiMessageSequence::createControllerUpdatesForTime (const int channelNumber, const double time, Array<MidiMessage>& dest) | |||
{ | |||
bool doneProg = false; | |||
bool donePitchWheel = false; | |||
Array<int> 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)); | |||
} | |||
} | |||
} | |||
@@ -247,7 +247,7 @@ public: | |||
state at the required time. | |||
*/ | |||
void createControllerUpdatesForTime (int channelNumber, double time, | |||
OwnedArray<MidiMessage>& resultMessages); | |||
Array<MidiMessage>& resultMessages); | |||
//============================================================================== | |||
/** Swaps this sequence with another one. */ | |||
@@ -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) | |||
@@ -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(); | |||
} |
@@ -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; | |||
@@ -92,7 +92,6 @@ AudioDeviceManager::AudioDeviceManager() | |||
: numInputChansNeeded (0), | |||
numOutputChansNeeded (2), | |||
listNeedsScanning (true), | |||
useInputNames (false), | |||
inputLevel (0), | |||
testSoundPosition (0), | |||
cpuUsageMs (0), | |||
@@ -154,13 +153,14 @@ static void addIfNotNull (OwnedArray<AudioIODeviceType>& list, AudioIODeviceType | |||
void AudioDeviceManager::createAudioDeviceTypes (OwnedArray<AudioIODeviceType>& 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()); | |||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_iOSAudio()); | |||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_JACK()); | |||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_ALSA()); | |||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_JACK()); | |||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_OpenSLES()); | |||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_Android()); | |||
} | |||
@@ -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); | |||
@@ -450,7 +450,6 @@ private: | |||
BigInteger inputChannels, outputChannels; | |||
ScopedPointer<XmlElement> lastExplicitSettings; | |||
mutable bool listNeedsScanning; | |||
bool useInputNames; | |||
Atomic<int> inputLevelMeasurementEnabledCount; | |||
double inputLevel; | |||
ScopedPointer<AudioSampleBuffer> testSound; | |||