- Library code update

- project update (added Eigen)

git-svn-id: http://moon:8086/svn/software/trunk/projects/mpsk_rx_gui@94 b431acfa-c32f-4a4a-93f1-934dc6c82436
This commit is contained in:
2014-12-20 20:59:48 +00:00
parent 1b4d8e9f54
commit dc5560392f
312 changed files with 6349 additions and 4195 deletions
+6 -6
View File
@@ -18,12 +18,12 @@ ifeq ($(CONFIG),Debug)
TARGET_ARCH := -march=native TARGET_ARCH := -march=native
endif endif
CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -D "radio_float_t=float" -D "fir_float_t=float" -D "JUCER_LINUX_MAKE_7346DA2A=1" -D "JUCE_APP_VERSION=1.0.0" -D "JUCE_APP_VERSION_HEX=0x10000" -I /usr/include -I /usr/include/freetype2 -I ../../JuceLibraryCode -I ../../JuceLibraryCode/modules -I ../../../../libsrc -I ../../Source CPPFLAGS := $(DEPFLAGS) -std=c++11 -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -D "radio_float_t=float" -D "fir_float_t=float" -D "JUCER_LINUX_MAKE_7346DA2A=1" -D "JUCE_APP_VERSION=1.0.0" -D "JUCE_APP_VERSION_HEX=0x10000" -I /usr/include -I /usr/include/freetype2 -I ../../JuceLibraryCode -I ../../JuceLibraryCode/modules -I ../../../../libsrc -I ../../../../libsrc/cpp -I ../../Source -I /usr/local/include/eigen3
CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -g -ggdb -O0 -std=c++11 -pg CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -g -ggdb -O0 -std=c++11 -pg
CXXFLAGS += $(CFLAGS) CXXFLAGS += $(CFLAGS)
LDFLAGS += $(TARGET_ARCH) -L$(BINDIR) -L$(LIBDIR) -L/usr/X11R6/lib/ -L../../../../libsrc/radio/build/linux -L../../../../libsrc/fir/build/linux -L../../../../libsrc/crc/build/linux -lGL -lX11 -lXext -lXinerama -lasound -ldl -lfreetype -lpthread -lrt -lradio -lfir -lcrc LDFLAGS += $(TARGET_ARCH) -L$(BINDIR) -L$(LIBDIR) -L/usr/X11R6/lib/ -L../../../../libsrc/build/linux -lGL -lX11 -lXext -lXinerama -lasound -ldl -lfreetype -lpthread -lrt -lradio -lfir -lcrc
LDDEPS := LDDEPS :=
RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -D "radio_float_t=float" -D "fir_float_t=float" -D "JUCER_LINUX_MAKE_7346DA2A=1" -D "JUCE_APP_VERSION=1.0.0" -D "JUCE_APP_VERSION_HEX=0x10000" -I /usr/include -I /usr/include/freetype2 -I ../../JuceLibraryCode -I ../../JuceLibraryCode/modules -I ../../../../libsrc -I ../../Source RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -D "radio_float_t=float" -D "fir_float_t=float" -D "JUCER_LINUX_MAKE_7346DA2A=1" -D "JUCE_APP_VERSION=1.0.0" -D "JUCE_APP_VERSION_HEX=0x10000" -I /usr/include -I /usr/include/freetype2 -I ../../JuceLibraryCode -I ../../JuceLibraryCode/modules -I ../../../../libsrc -I ../../../../libsrc/cpp -I ../../Source -I /usr/local/include/eigen3
TARGET := M-PSK\ Receiver TARGET := M-PSK\ Receiver
BLDCMD = $(CXX) -o $(OUTDIR)/$(TARGET) $(OBJECTS) $(LDFLAGS) $(RESOURCES) $(TARGET_ARCH) BLDCMD = $(CXX) -o $(OUTDIR)/$(TARGET) $(OBJECTS) $(LDFLAGS) $(RESOURCES) $(TARGET_ARCH)
CLEANCMD = rm -rf $(OUTDIR)/$(TARGET) $(OBJDIR) CLEANCMD = rm -rf $(OUTDIR)/$(TARGET) $(OBJDIR)
@@ -39,12 +39,12 @@ ifeq ($(CONFIG),Release)
TARGET_ARCH := -march=native TARGET_ARCH := -march=native
endif endif
CPPFLAGS := $(DEPFLAGS) -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -D "radio_float_t=float" -D "fir_float_t=float" -D "JUCER_LINUX_MAKE_7346DA2A=1" -D "JUCE_APP_VERSION=1.0.0" -D "JUCE_APP_VERSION_HEX=0x10000" -I /usr/include -I /usr/include/freetype2 -I ../../JuceLibraryCode -I ../../JuceLibraryCode/modules -I ../../../../libsrc -I ../../Source CPPFLAGS := $(DEPFLAGS) -std=c++11 -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -D "radio_float_t=float" -D "fir_float_t=float" -D "JUCER_LINUX_MAKE_7346DA2A=1" -D "JUCE_APP_VERSION=1.0.0" -D "JUCE_APP_VERSION_HEX=0x10000" -I /usr/include -I /usr/include/freetype2 -I ../../JuceLibraryCode -I ../../JuceLibraryCode/modules -I ../../../../libsrc -I ../../../../libsrc/cpp -I ../../Source -I /usr/local/include/eigen3
CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -g -ggdb -O3 -std=c++11 -pg CFLAGS += $(CPPFLAGS) $(TARGET_ARCH) -g -ggdb -O3 -std=c++11 -pg
CXXFLAGS += $(CFLAGS) CXXFLAGS += $(CFLAGS)
LDFLAGS += $(TARGET_ARCH) -L$(BINDIR) -L$(LIBDIR) -L/usr/X11R6/lib/ -L../../../../libsrc/radio/build/linux -L../../../../libsrc/fir/build/linux -L../../../../libsrc/crc/build/linux -lGL -lX11 -lXext -lXinerama -lasound -ldl -lfreetype -lpthread -lrt -lradio -lfir -lcrc LDFLAGS += $(TARGET_ARCH) -L$(BINDIR) -L$(LIBDIR) -L/usr/X11R6/lib/ -L../../../../libsrc/build/linux -lGL -lX11 -lXext -lXinerama -lasound -ldl -lfreetype -lpthread -lrt -lradio -lfir -lcrc
LDDEPS := LDDEPS :=
RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -D "radio_float_t=float" -D "fir_float_t=float" -D "JUCER_LINUX_MAKE_7346DA2A=1" -D "JUCE_APP_VERSION=1.0.0" -D "JUCE_APP_VERSION_HEX=0x10000" -I /usr/include -I /usr/include/freetype2 -I ../../JuceLibraryCode -I ../../JuceLibraryCode/modules -I ../../../../libsrc -I ../../Source RESFLAGS := -D "LINUX=1" -D "DEBUG=1" -D "_DEBUG=1" -D "radio_float_t=float" -D "fir_float_t=float" -D "JUCER_LINUX_MAKE_7346DA2A=1" -D "JUCE_APP_VERSION=1.0.0" -D "JUCE_APP_VERSION_HEX=0x10000" -I /usr/include -I /usr/include/freetype2 -I ../../JuceLibraryCode -I ../../JuceLibraryCode/modules -I ../../../../libsrc -I ../../../../libsrc/cpp -I ../../Source -I /usr/local/include/eigen3
TARGET := M-PSK\ Receiver TARGET := M-PSK\ Receiver
BLDCMD = $(CXX) -o $(OUTDIR)/$(TARGET) $(OBJECTS) $(LDFLAGS) $(RESOURCES) $(TARGET_ARCH) BLDCMD = $(CXX) -o $(OUTDIR)/$(TARGET) $(OBJECTS) $(LDFLAGS) $(RESOURCES) $(TARGET_ARCH)
CLEANCMD = rm -rf $(OUTDIR)/$(TARGET) $(OBJDIR) CLEANCMD = rm -rf $(OUTDIR)/$(TARGET) $(OBJDIR)
+2
View File
@@ -35,11 +35,13 @@
using namespace juce; using namespace juce;
#endif #endif
#if ! JUCE_DONT_DECLARE_PROJECTINFO
namespace ProjectInfo namespace ProjectInfo
{ {
const char* const projectName = "M-QAM Transceiver"; const char* const projectName = "M-QAM Transceiver";
const char* const versionString = "1.0.0"; const char* const versionString = "1.0.0";
const int versionNumber = 0x10000; const int versionNumber = 0x10000;
} }
#endif
#endif // __APPHEADERFILE_GMZMR9__ #endif // __APPHEADERFILE_GMZMR9__
@@ -25,7 +25,7 @@
void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest, int numSamples, const int destBytesPerSample) void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
{ {
const double maxVal = (double) 0x7fff; const double maxVal = (double) 0x7fff;
char* intData = static_cast <char*> (dest); char* intData = static_cast<char*> (dest);
if (dest != (void*) source || destBytesPerSample <= 4) if (dest != (void*) source || destBytesPerSample <= 4)
{ {
@@ -50,7 +50,7 @@ void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest
void AudioDataConverters::convertFloatToInt16BE (const float* source, void* dest, int numSamples, const int destBytesPerSample) void AudioDataConverters::convertFloatToInt16BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
{ {
const double maxVal = (double) 0x7fff; const double maxVal = (double) 0x7fff;
char* intData = static_cast <char*> (dest); char* intData = static_cast<char*> (dest);
if (dest != (void*) source || destBytesPerSample <= 4) if (dest != (void*) source || destBytesPerSample <= 4)
{ {
@@ -75,7 +75,7 @@ void AudioDataConverters::convertFloatToInt16BE (const float* source, void* dest
void AudioDataConverters::convertFloatToInt24LE (const float* source, void* dest, int numSamples, const int destBytesPerSample) void AudioDataConverters::convertFloatToInt24LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
{ {
const double maxVal = (double) 0x7fffff; const double maxVal = (double) 0x7fffff;
char* intData = static_cast <char*> (dest); char* intData = static_cast<char*> (dest);
if (dest != (void*) source || destBytesPerSample <= 4) if (dest != (void*) source || destBytesPerSample <= 4)
{ {
@@ -100,7 +100,7 @@ void AudioDataConverters::convertFloatToInt24LE (const float* source, void* dest
void AudioDataConverters::convertFloatToInt24BE (const float* source, void* dest, int numSamples, const int destBytesPerSample) void AudioDataConverters::convertFloatToInt24BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
{ {
const double maxVal = (double) 0x7fffff; const double maxVal = (double) 0x7fffff;
char* intData = static_cast <char*> (dest); char* intData = static_cast<char*> (dest);
if (dest != (void*) source || destBytesPerSample <= 4) if (dest != (void*) source || destBytesPerSample <= 4)
{ {
@@ -125,7 +125,7 @@ void AudioDataConverters::convertFloatToInt24BE (const float* source, void* dest
void AudioDataConverters::convertFloatToInt32LE (const float* source, void* dest, int numSamples, const int destBytesPerSample) void AudioDataConverters::convertFloatToInt32LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
{ {
const double maxVal = (double) 0x7fffffff; const double maxVal = (double) 0x7fffffff;
char* intData = static_cast <char*> (dest); char* intData = static_cast<char*> (dest);
if (dest != (void*) source || destBytesPerSample <= 4) if (dest != (void*) source || destBytesPerSample <= 4)
{ {
@@ -150,7 +150,7 @@ void AudioDataConverters::convertFloatToInt32LE (const float* source, void* dest
void AudioDataConverters::convertFloatToInt32BE (const float* source, void* dest, int numSamples, const int destBytesPerSample) void AudioDataConverters::convertFloatToInt32BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
{ {
const double maxVal = (double) 0x7fffffff; const double maxVal = (double) 0x7fffffff;
char* intData = static_cast <char*> (dest); char* intData = static_cast<char*> (dest);
if (dest != (void*) source || destBytesPerSample <= 4) if (dest != (void*) source || destBytesPerSample <= 4)
{ {
@@ -176,7 +176,7 @@ void AudioDataConverters::convertFloatToFloat32LE (const float* source, void* de
{ {
jassert (dest != (void*) source || destBytesPerSample <= 4); // This op can't be performed on in-place data! jassert (dest != (void*) source || destBytesPerSample <= 4); // This op can't be performed on in-place data!
char* d = static_cast <char*> (dest); char* d = static_cast<char*> (dest);
for (int i = 0; i < numSamples; ++i) for (int i = 0; i < numSamples; ++i)
{ {
@@ -194,7 +194,7 @@ void AudioDataConverters::convertFloatToFloat32BE (const float* source, void* de
{ {
jassert (dest != (void*) source || destBytesPerSample <= 4); // This op can't be performed on in-place data! jassert (dest != (void*) source || destBytesPerSample <= 4); // This op can't be performed on in-place data!
char* d = static_cast <char*> (dest); char* d = static_cast<char*> (dest);
for (int i = 0; i < numSamples; ++i) for (int i = 0; i < numSamples; ++i)
{ {
@@ -212,7 +212,7 @@ void AudioDataConverters::convertFloatToFloat32BE (const float* source, void* de
void AudioDataConverters::convertInt16LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) void AudioDataConverters::convertInt16LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{ {
const float scale = 1.0f / 0x7fff; const float scale = 1.0f / 0x7fff;
const char* intData = static_cast <const char*> (source); const char* intData = static_cast<const char*> (source);
if (source != (void*) dest || srcBytesPerSample >= 4) if (source != (void*) dest || srcBytesPerSample >= 4)
{ {
@@ -237,7 +237,7 @@ void AudioDataConverters::convertInt16LEToFloat (const void* const source, float
void AudioDataConverters::convertInt16BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) void AudioDataConverters::convertInt16BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{ {
const float scale = 1.0f / 0x7fff; const float scale = 1.0f / 0x7fff;
const char* intData = static_cast <const char*> (source); const char* intData = static_cast<const char*> (source);
if (source != (void*) dest || srcBytesPerSample >= 4) if (source != (void*) dest || srcBytesPerSample >= 4)
{ {
@@ -262,7 +262,7 @@ void AudioDataConverters::convertInt16BEToFloat (const void* const source, float
void AudioDataConverters::convertInt24LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) void AudioDataConverters::convertInt24LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{ {
const float scale = 1.0f / 0x7fffff; const float scale = 1.0f / 0x7fffff;
const char* intData = static_cast <const char*> (source); const char* intData = static_cast<const char*> (source);
if (source != (void*) dest || srcBytesPerSample >= 4) if (source != (void*) dest || srcBytesPerSample >= 4)
{ {
@@ -287,7 +287,7 @@ void AudioDataConverters::convertInt24LEToFloat (const void* const source, float
void AudioDataConverters::convertInt24BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) void AudioDataConverters::convertInt24BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{ {
const float scale = 1.0f / 0x7fffff; const float scale = 1.0f / 0x7fffff;
const char* intData = static_cast <const char*> (source); const char* intData = static_cast<const char*> (source);
if (source != (void*) dest || srcBytesPerSample >= 4) if (source != (void*) dest || srcBytesPerSample >= 4)
{ {
@@ -312,7 +312,7 @@ void AudioDataConverters::convertInt24BEToFloat (const void* const source, float
void AudioDataConverters::convertInt32LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) void AudioDataConverters::convertInt32LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{ {
const float scale = 1.0f / 0x7fffffff; const float scale = 1.0f / 0x7fffffff;
const char* intData = static_cast <const char*> (source); const char* intData = static_cast<const char*> (source);
if (source != (void*) dest || srcBytesPerSample >= 4) if (source != (void*) dest || srcBytesPerSample >= 4)
{ {
@@ -337,7 +337,7 @@ void AudioDataConverters::convertInt32LEToFloat (const void* const source, float
void AudioDataConverters::convertInt32BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) void AudioDataConverters::convertInt32BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{ {
const float scale = 1.0f / 0x7fffffff; const float scale = 1.0f / 0x7fffffff;
const char* intData = static_cast <const char*> (source); const char* intData = static_cast<const char*> (source);
if (source != (void*) dest || srcBytesPerSample >= 4) if (source != (void*) dest || srcBytesPerSample >= 4)
{ {
@@ -361,7 +361,7 @@ void AudioDataConverters::convertInt32BEToFloat (const void* const source, float
void AudioDataConverters::convertFloat32LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) void AudioDataConverters::convertFloat32LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{ {
const char* s = static_cast <const char*> (source); const char* s = static_cast<const char*> (source);
for (int i = 0; i < numSamples; ++i) for (int i = 0; i < numSamples; ++i)
{ {
@@ -378,7 +378,7 @@ void AudioDataConverters::convertFloat32LEToFloat (const void* const source, flo
void AudioDataConverters::convertFloat32BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) void AudioDataConverters::convertFloat32BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{ {
const char* s = static_cast <const char*> (source); const char* s = static_cast<const char*> (source);
for (int i = 0; i < numSamples; ++i) for (int i = 0; i < numSamples; ++i)
{ {
@@ -91,7 +91,7 @@ AudioSampleBuffer::AudioSampleBuffer (float* const* dataToReferTo,
allocatedBytes (0) allocatedBytes (0)
{ {
jassert (dataToReferTo != nullptr); jassert (dataToReferTo != nullptr);
jassert (numChans >= 0); jassert (numChans >= 0 && numSamples >= 0);
allocateChannels (dataToReferTo, 0); allocateChannels (dataToReferTo, 0);
} }
@@ -105,7 +105,7 @@ AudioSampleBuffer::AudioSampleBuffer (float* const* dataToReferTo,
isClear (false) isClear (false)
{ {
jassert (dataToReferTo != nullptr); jassert (dataToReferTo != nullptr);
jassert (numChans >= 0); jassert (numChans >= 0 && startSample >= 0 && numSamples >= 0);
allocateChannels (dataToReferTo, startSample); allocateChannels (dataToReferTo, startSample);
} }
@@ -114,7 +114,7 @@ void AudioSampleBuffer::setDataToReferTo (float** dataToReferTo,
const int newNumSamples) noexcept const int newNumSamples) noexcept
{ {
jassert (dataToReferTo != nullptr); jassert (dataToReferTo != nullptr);
jassert (newNumChannels >= 0); jassert (newNumChannels >= 0 && newNumSamples >= 0);
allocatedBytes = 0; allocatedBytes = 0;
allocatedData.free(); allocatedData.free();
@@ -128,6 +128,8 @@ void AudioSampleBuffer::setDataToReferTo (float** dataToReferTo,
void AudioSampleBuffer::allocateChannels (float* const* const dataToReferTo, int offset) void AudioSampleBuffer::allocateChannels (float* const* const dataToReferTo, int offset)
{ {
jassert (offset >= 0);
// (try to avoid doing a malloc here, as that'll blow up things like Pro-Tools) // (try to avoid doing a malloc here, as that'll blow up things like Pro-Tools)
if (numChannels < (int) numElementsInArray (preallocatedChannelSpace)) if (numChannels < (int) numElementsInArray (preallocatedChannelSpace))
{ {
@@ -163,6 +165,8 @@ AudioSampleBuffer& AudioSampleBuffer::operator= (const AudioSampleBuffer& other)
} }
else else
{ {
isClear = false;
for (int i = 0; i < numChannels; ++i) for (int i = 0; i < numChannels; ++i)
FloatVectorOperations::copy (channels[i], other.channels[i], size); FloatVectorOperations::copy (channels[i], other.channels[i], size);
} }
@@ -24,8 +24,9 @@
namespace FloatVectorHelpers namespace FloatVectorHelpers
{ {
#define JUCE_INCREMENT_SRC_DEST dest += (16 / sizeof (*dest)); src += (16 / sizeof (*dest)); #define JUCE_INCREMENT_SRC_DEST dest += (16 / sizeof (*dest)); src += (16 / sizeof (*dest));
#define JUCE_INCREMENT_DEST dest += (16 / sizeof (*dest)); #define JUCE_INCREMENT_SRC1_SRC2_DEST dest += (16 / sizeof (*dest)); src1 += (16 / sizeof (*dest)); src2 += (16 / sizeof (*dest));
#define JUCE_INCREMENT_DEST dest += (16 / sizeof (*dest));
#if JUCE_USE_SSE_INTRINSICS #if JUCE_USE_SSE_INTRINSICS
static bool sse2Present = false; static bool sse2Present = false;
@@ -122,6 +123,17 @@ namespace FloatVectorHelpers
} \ } \
JUCE_FINISH_VEC_OP (normalOp) JUCE_FINISH_VEC_OP (normalOp)
#define JUCE_PERFORM_VEC_OP_SRC1_SRC2_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; \
void (&storeDst) (Mode::Type* dest, Mode::ParallelType a) = FloatVectorHelpers::isAligned (dest) ? Mode::storeA : Mode::storeU; \
JUCE_VEC_LOOP_TWO_SOURCES (vecOp, loadSrc1, loadSrc2, storeDst, locals, increment); \
} \
JUCE_FINISH_VEC_OP (normalOp)
//============================================================================== //==============================================================================
#elif JUCE_USE_ARM_NEON #elif JUCE_USE_ARM_NEON
@@ -193,6 +205,12 @@ namespace FloatVectorHelpers
JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \
JUCE_FINISH_VEC_OP (normalOp) JUCE_FINISH_VEC_OP (normalOp)
#define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \
JUCE_BEGIN_VEC_OP \
setupOp \
JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \
JUCE_FINISH_VEC_OP (normalOp)
//============================================================================== //==============================================================================
#else #else
#define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \ #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \
@@ -201,6 +219,8 @@ namespace FloatVectorHelpers
#define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \ #define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \
for (int i = 0; i < num; ++i) normalOp; for (int i = 0; i < num; ++i) normalOp;
#define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \
for (int i = 0; i < num; ++i) normalOp;
#endif #endif
//============================================================================== //==============================================================================
@@ -212,10 +232,19 @@ namespace FloatVectorHelpers
increment; \ increment; \
} }
#define JUCE_VEC_LOOP_TWO_SOURCES(vecOp, src1Load, src2Load, dstStore, locals, increment) \
for (int i = 0; i < numLongOps; ++i) \
{ \
locals (src1Load, src2Load); \
dstStore (dest, vecOp); \
increment; \
}
#define JUCE_LOAD_NONE(srcLoad, dstLoad) #define JUCE_LOAD_NONE(srcLoad, dstLoad)
#define JUCE_LOAD_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest); #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_SRC(srcLoad, dstLoad) const Mode::ParallelType s = srcLoad (src);
#define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest), 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);
#if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON
template<int typeSize> struct ModeType { typedef BasicOps32 Mode; }; template<int typeSize> struct ModeType { typedef BasicOps32 Mode; };
@@ -229,7 +258,7 @@ namespace FloatVectorHelpers
static Type findMinOrMax (const Type* src, int num, const bool isMinimum) noexcept static Type findMinOrMax (const Type* src, int num, const bool isMinimum) noexcept
{ {
const int numLongOps = num / Mode::numParallel; int numLongOps = num / Mode::numParallel;
#if JUCE_USE_SSE_INTRINSICS #if JUCE_USE_SSE_INTRINSICS
if (numLongOps > 1 && isSSE2Available()) if (numLongOps > 1 && isSSE2Available())
@@ -246,7 +275,7 @@ namespace FloatVectorHelpers
if (isMinimum) if (isMinimum)
{ {
for (int i = 1; i < numLongOps; ++i) while (--numLongOps > 0)
{ {
src += Mode::numParallel; src += Mode::numParallel;
val = Mode::min (val, Mode::loadA (src)); val = Mode::min (val, Mode::loadA (src));
@@ -254,7 +283,7 @@ namespace FloatVectorHelpers
} }
else else
{ {
for (int i = 1; i < numLongOps; ++i) while (--numLongOps > 0)
{ {
src += Mode::numParallel; src += Mode::numParallel;
val = Mode::max (val, Mode::loadA (src)); val = Mode::max (val, Mode::loadA (src));
@@ -268,7 +297,7 @@ namespace FloatVectorHelpers
if (isMinimum) if (isMinimum)
{ {
for (int i = 1; i < numLongOps; ++i) while (--numLongOps > 0)
{ {
src += Mode::numParallel; src += Mode::numParallel;
val = Mode::min (val, Mode::loadU (src)); val = Mode::min (val, Mode::loadU (src));
@@ -276,7 +305,7 @@ namespace FloatVectorHelpers
} }
else else
{ {
for (int i = 1; i < numLongOps; ++i) while (--numLongOps > 0)
{ {
src += Mode::numParallel; src += Mode::numParallel;
val = Mode::max (val, Mode::loadU (src)); val = Mode::max (val, Mode::loadU (src));
@@ -288,6 +317,7 @@ namespace FloatVectorHelpers
: Mode::max (val); : Mode::max (val);
num &= (Mode::numParallel - 1); num &= (Mode::numParallel - 1);
src += Mode::numParallel;
for (int i = 0; i < num; ++i) for (int i = 0; i < num; ++i)
result = isMinimum ? jmin (result, src[i]) result = isMinimum ? jmin (result, src[i])
@@ -302,7 +332,7 @@ namespace FloatVectorHelpers
static Range<Type> findMinAndMax (const Type* src, int num) noexcept static Range<Type> findMinAndMax (const Type* src, int num) noexcept
{ {
const int numLongOps = num / Mode::numParallel; int numLongOps = num / Mode::numParallel;
#if JUCE_USE_SSE_INTRINSICS #if JUCE_USE_SSE_INTRINSICS
if (numLongOps > 1 && isSSE2Available()) if (numLongOps > 1 && isSSE2Available())
@@ -318,7 +348,7 @@ namespace FloatVectorHelpers
mn = Mode::loadA (src); mn = Mode::loadA (src);
mx = mn; mx = mn;
for (int i = 1; i < numLongOps; ++i) while (--numLongOps > 0)
{ {
src += Mode::numParallel; src += Mode::numParallel;
const ParallelType v = Mode::loadA (src); const ParallelType v = Mode::loadA (src);
@@ -332,7 +362,7 @@ namespace FloatVectorHelpers
mn = Mode::loadU (src); mn = Mode::loadU (src);
mx = mn; mx = mn;
for (int i = 1; i < numLongOps; ++i) while (--numLongOps > 0)
{ {
src += Mode::numParallel; src += Mode::numParallel;
const ParallelType v = Mode::loadU (src); const ParallelType v = Mode::loadU (src);
@@ -344,7 +374,9 @@ namespace FloatVectorHelpers
Range<Type> result (Mode::min (mn), Range<Type> result (Mode::min (mn),
Mode::max (mx)); Mode::max (mx));
num &= 3; num &= (Mode::numParallel - 1);
src += Mode::numParallel;
for (int i = 0; i < num; ++i) for (int i = 0; i < num; ++i)
result = result.getUnionWith (src[i]); result = result.getUnionWith (src[i]);
@@ -409,7 +441,7 @@ void JUCE_CALLTYPE FloatVectorOperations::copy (double* dest, const double* src,
void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept
{ {
#if JUCE_USE_VDSP_FRAMEWORK #if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsmul (src, 1, &multiplier, dest, 1, num); vDSP_vsmul (src, 1, &multiplier, dest, 1, (vDSP_Length) num);
#else #else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s),
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
@@ -420,7 +452,7 @@ void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (float* dest, const f
void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept
{ {
#if JUCE_USE_VDSP_FRAMEWORK #if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsmulD (src, 1, &multiplier, dest, 1, num); vDSP_vsmulD (src, 1, &multiplier, dest, 1, (vDSP_Length) num);
#else #else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s),
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
@@ -440,10 +472,32 @@ void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, double amount, int
const Mode::ParallelType amountToAdd = Mode::load1 (amount);) const Mode::ParallelType amountToAdd = Mode::load1 (amount);)
} }
void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float* src, float amount, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsadd (src, 1, &amount, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] + amount, Mode::add (am, s),
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
const Mode::ParallelType am = Mode::load1 (amount);)
#endif
}
void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, double* src, double amount, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsaddD (src, 1, &amount, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] + amount, Mode::add (am, s),
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
const Mode::ParallelType am = Mode::load1 (amount);)
#endif
}
void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src, int num) noexcept void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src, int num) noexcept
{ {
#if JUCE_USE_VDSP_FRAMEWORK #if JUCE_USE_VDSP_FRAMEWORK
vDSP_vadd (src, 1, dest, 1, dest, 1, num); vDSP_vadd (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
#else #else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
#endif #endif
@@ -452,16 +506,34 @@ void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src, in
void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src, int num) noexcept void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src, int num) noexcept
{ {
#if JUCE_USE_VDSP_FRAMEWORK #if JUCE_USE_VDSP_FRAMEWORK
vDSP_vaddD (src, 1, dest, 1, dest, 1, num); vDSP_vaddD (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
#else #else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
#endif #endif
} }
void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src1, const float* src2, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vadd (src1, 1, src2, 1, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] + src2[i], Mode::add (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, )
#endif
}
void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src1, const double* src2, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vaddD (src1, 1, src2, 1, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] + src2[i], Mode::add (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, )
#endif
}
void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* src, int num) noexcept void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* src, int num) noexcept
{ {
#if JUCE_USE_VDSP_FRAMEWORK #if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsub (src, 1, dest, 1, dest, 1, num); vDSP_vsub (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
#else #else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
#endif #endif
@@ -470,12 +542,30 @@ void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* sr
void JUCE_CALLTYPE FloatVectorOperations::subtract (double* dest, const double* src, int num) noexcept void JUCE_CALLTYPE FloatVectorOperations::subtract (double* dest, const double* src, int num) noexcept
{ {
#if JUCE_USE_VDSP_FRAMEWORK #if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsubD (src, 1, dest, 1, dest, 1, num); vDSP_vsubD (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
#else #else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
#endif #endif
} }
void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* src1, const float* src2, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsub (src2, 1, src1, 1, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] - src2[i], Mode::sub (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, )
#endif
}
void JUCE_CALLTYPE FloatVectorOperations::subtract (double* dest, const double* src1, const double* src2, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsubD (src2, 1, src1, 1, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] - src2[i], Mode::sub (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, )
#endif
}
void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept
{ {
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)), JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)),
@@ -493,7 +583,7 @@ void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (double* dest, const d
void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, int num) noexcept void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, int num) noexcept
{ {
#if JUCE_USE_VDSP_FRAMEWORK #if JUCE_USE_VDSP_FRAMEWORK
vDSP_vmul (src, 1, dest, 1, dest, 1, num); vDSP_vmul (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
#else #else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
#endif #endif
@@ -502,16 +592,34 @@ void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* sr
void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src, int num) noexcept void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src, int num) noexcept
{ {
#if JUCE_USE_VDSP_FRAMEWORK #if JUCE_USE_VDSP_FRAMEWORK
vDSP_vmulD (src, 1, dest, 1, dest, 1, num); vDSP_vmulD (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
#else #else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
#endif #endif
} }
void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src1, const float* src2, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vmul (src1, 1, src2, 1, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] * src2[i], Mode::mul (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, )
#endif
}
void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src1, const double* src2, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vmulD (src1, 1, src2, 1, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] * src2[i], Mode::mul (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, )
#endif
}
void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, float multiplier, int num) noexcept void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, float multiplier, int num) noexcept
{ {
#if JUCE_USE_VDSP_FRAMEWORK #if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsmul (dest, 1, &multiplier, dest, 1, num); vDSP_vsmul (dest, 1, &multiplier, dest, 1, (vDSP_Length) num);
#else #else
JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST, JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST,
const Mode::ParallelType mult = Mode::load1 (multiplier);) const Mode::ParallelType mult = Mode::load1 (multiplier);)
@@ -521,13 +629,27 @@ void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, float multiplie
void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, double multiplier, int num) noexcept void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, double multiplier, int num) noexcept
{ {
#if JUCE_USE_VDSP_FRAMEWORK #if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsmulD (dest, 1, &multiplier, dest, 1, num); vDSP_vsmulD (dest, 1, &multiplier, dest, 1, (vDSP_Length) num);
#else #else
JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST, JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST,
const Mode::ParallelType mult = Mode::load1 (multiplier);) const Mode::ParallelType mult = Mode::load1 (multiplier);)
#endif #endif
} }
void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, float multiplier, int num) noexcept
{
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s),
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
const Mode::ParallelType mult = Mode::load1 (multiplier);)
}
void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src, double multiplier, int num) noexcept
{
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s),
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
const Mode::ParallelType mult = Mode::load1 (multiplier);)
}
void FloatVectorOperations::negate (float* dest, const float* src, int num) noexcept void FloatVectorOperations::negate (float* dest, const float* src, int num) noexcept
{ {
#if JUCE_USE_VDSP_FRAMEWORK #if JUCE_USE_VDSP_FRAMEWORK
@@ -640,8 +762,8 @@ public:
const int range = random.nextBool() ? 500 : 10; const int range = random.nextBool() ? 500 : 10;
const int num = random.nextInt (range) + 1; const int num = random.nextInt (range) + 1;
HeapBlock<ValueType> buffer1 (num + 16), buffer2 (num + 16); HeapBlock<ValueType> buffer1 ((size_t) num + 16), buffer2 ((size_t) num + 16);
HeapBlock<int> buffer3 (num + 16); HeapBlock<int> buffer3 ((size_t) num + 16);
#if JUCE_ARM #if JUCE_ARM
ValueType* const data1 = buffer1; ValueType* const data1 = buffer1;
@@ -65,18 +65,36 @@ public:
/** Adds a fixed value to the destination values. */ /** Adds a fixed value to the destination values. */
static void JUCE_CALLTYPE add (double* dest, double amountToAdd, int numValues) noexcept; static void JUCE_CALLTYPE add (double* dest, double amountToAdd, int numValues) noexcept;
/** Adds a fixed value to each source value and stores it in the destination array. */
static void JUCE_CALLTYPE add (float* dest, float* src, float amount, int numValues) noexcept;
/** Adds a fixed value to each source value and stores it in the destination array. */
static void JUCE_CALLTYPE add (double* dest, double* src, double amount, int numValues) noexcept;
/** Adds the source values to the destination values. */ /** Adds the source values to the destination values. */
static void JUCE_CALLTYPE add (float* dest, const float* src, int numValues) noexcept; static void JUCE_CALLTYPE add (float* dest, const float* src, int numValues) noexcept;
/** Adds the source values to the destination values. */ /** Adds the source values to the destination values. */
static void JUCE_CALLTYPE add (double* dest, const double* src, int numValues) noexcept; static void JUCE_CALLTYPE add (double* dest, const double* src, int numValues) noexcept;
/** Adds each source1 value to the corresponding source2 value and stores the result in the destination array. */
static void JUCE_CALLTYPE add (float* dest, const float* src1, const float* src2, int num) noexcept;
/** Adds each source1 value to the corresponding source2 value and stores the result in the destination array. */
static void JUCE_CALLTYPE add (double* dest, const double* src1, const double* src2, int num) noexcept;
/** Subtracts the source values from the destination values. */ /** Subtracts the source values from the destination values. */
static void JUCE_CALLTYPE subtract (float* dest, const float* src, int numValues) noexcept; static void JUCE_CALLTYPE subtract (float* dest, const float* src, int numValues) noexcept;
/** Subtracts the source values from the destination values. */ /** Subtracts the source values from the destination values. */
static void JUCE_CALLTYPE subtract (double* dest, const double* src, int numValues) noexcept; static void JUCE_CALLTYPE subtract (double* dest, const double* src, int numValues) noexcept;
/** Subtracts each source2 value from the corresponding source1 value and stores the result in the destination array. */
static void JUCE_CALLTYPE subtract (float* dest, const float* src1, const float* src2, int num) noexcept;
/** Subtracts each source2 value from the corresponding source1 value and stores the result in the destination array. */
static void JUCE_CALLTYPE subtract (double* dest, const double* src1, const double* src2, int num) noexcept;
/** Multiplies each source value by the given multiplier, then adds it to the destination value. */ /** 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; static void JUCE_CALLTYPE addWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept;
@@ -89,12 +107,24 @@ public:
/** Multiplies the destination values by the source values. */ /** Multiplies the destination values by the source values. */
static void JUCE_CALLTYPE multiply (double* dest, const double* src, int numValues) noexcept; static void JUCE_CALLTYPE multiply (double* dest, const double* src, int numValues) noexcept;
/** Multiplies each source1 value by the correspinding source2 value, then stores it in the destination array. */
static void JUCE_CALLTYPE multiply (float* dest, const float* src1, const float* src2, int numValues) noexcept;
/** Multiplies each source1 value by the correspinding source2 value, then stores it in the destination array. */
static void JUCE_CALLTYPE multiply (double* dest, const double* src1, const double* src2, int numValues) noexcept;
/** Multiplies each of the destination values by a fixed multiplier. */ /** Multiplies each of the destination values by a fixed multiplier. */
static void JUCE_CALLTYPE multiply (float* dest, float multiplier, int numValues) noexcept; static void JUCE_CALLTYPE multiply (float* dest, float multiplier, int numValues) noexcept;
/** Multiplies each of the destination values by a fixed multiplier. */ /** Multiplies each of the destination values by a fixed multiplier. */
static void JUCE_CALLTYPE multiply (double* dest, double multiplier, int numValues) noexcept; static void JUCE_CALLTYPE multiply (double* dest, double multiplier, int numValues) noexcept;
/** Multiplies each of the source values by a fixed multiplier and stores the result in the destination array. */
static void JUCE_CALLTYPE multiply (float* dest, const float* src, float multiplier, int num) noexcept;
/** Multiplies each of the source values by a fixed multiplier and stores the result in the destination array. */
static void JUCE_CALLTYPE multiply (double* dest, const double* src, double multiplier, int num) noexcept;
/** Copies a source vector to a destination, negating each value. */ /** Copies a source vector to a destination, negating each value. */
static void JUCE_CALLTYPE negate (float* dest, const float* src, int numValues) noexcept; static void JUCE_CALLTYPE negate (float* dest, const float* src, int numValues) noexcept;
@@ -181,7 +181,7 @@ public:
for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series
output = allPass[0][j].process (output); output = allPass[0][j].process (output);
samples[i] = output * wet1 + input * dry; samples[i] = output * wet1 + samples[i] * dry;
} }
} }
@@ -57,7 +57,9 @@
#endif #endif
#if (JUCE_MAC || JUCE_IOS) && JUCE_USE_VDSP_FRAMEWORK #if (JUCE_MAC || JUCE_IOS) && JUCE_USE_VDSP_FRAMEWORK
#define Point CarbonDummyPointName // (workaround to avoid definition of "Point" by old Carbon headers)
#include <Accelerate/Accelerate.h> #include <Accelerate/Accelerate.h>
#undef Point
#else #else
#undef JUCE_USE_VDSP_FRAMEWORK #undef JUCE_USE_VDSP_FRAMEWORK
#endif #endif
@@ -1,7 +1,7 @@
{ {
"id": "juce_audio_basics", "id": "juce_audio_basics",
"name": "JUCE audio and midi data classes", "name": "JUCE audio and midi data classes",
"version": "3.0.5", "version": "3.0.8",
"description": "Classes for audio buffer manipulation, midi message handling, synthesis, etc", "description": "Classes for audio buffer manipulation, midi message handling, synthesis, etc",
"website": "http://www.juce.com/juce", "website": "http://www.juce.com/juce",
"license": "GPL/Commercial", "license": "GPL/Commercial",
@@ -210,7 +210,7 @@ bool MidiBuffer::Iterator::getNextEvent (const uint8* &midiData, int& numBytes,
const int itemSize = MidiBufferHelpers::getEventDataSize (data); const int itemSize = MidiBufferHelpers::getEventDataSize (data);
numBytes = itemSize; numBytes = itemSize;
midiData = data + sizeof (int32) + sizeof (uint16); midiData = data + sizeof (int32) + sizeof (uint16);
data += sizeof (int32) + sizeof (uint16) + itemSize; data += sizeof (int32) + sizeof (uint16) + (size_t) itemSize;
return true; return true;
} }
@@ -223,7 +223,7 @@ bool MidiBuffer::Iterator::getNextEvent (MidiMessage& result, int& samplePositio
samplePosition = MidiBufferHelpers::getEventTime (data); samplePosition = MidiBufferHelpers::getEventTime (data);
const int itemSize = MidiBufferHelpers::getEventDataSize (data); const int itemSize = MidiBufferHelpers::getEventDataSize (data);
result = MidiMessage (data + sizeof (int32) + sizeof (uint16), itemSize, samplePosition); result = MidiMessage (data + sizeof (int32) + sizeof (uint16), itemSize, samplePosition);
data += sizeof (int32) + sizeof (uint16) + itemSize; data += sizeof (int32) + sizeof (uint16) + (size_t) itemSize;
return true; return true;
} }
@@ -186,9 +186,10 @@ public:
/** Retrieves a copy of the next event from the buffer. /** Retrieves a copy of the next event from the buffer.
@param result on return, this will be the message (the MidiMessage's timestamp @param result on return, this will be the message. The MidiMessage's timestamp
is not set) is set to the same value as samplePosition.
@param samplePosition on return, this will be the position of the event @param samplePosition on return, this will be the position of the event, as a
sample index in the buffer
@returns true if an event was found, or false if the iterator has reached @returns true if an event was found, or false if the iterator has reached
the end of the buffer the end of the buffer
*/ */
@@ -203,7 +204,8 @@ public:
temporarily until the MidiBuffer is altered. temporarily until the MidiBuffer is altered.
@param numBytesOfMidiData on return, this is the number of bytes of data used by the @param numBytesOfMidiData on return, this is the number of bytes of data used by the
midi message midi message
@param samplePosition on return, this will be the position of the event @param samplePosition on return, this will be the position of the event, as a
sample index in the buffer
@returns true if an event was found, or false if the iterator has reached @returns true if an event was found, or false if the iterator has reached
the end of the buffer the end of the buffer
*/ */
@@ -129,7 +129,7 @@ MidiMessage::MidiMessage (const MidiMessage& other)
{ {
if (other.allocatedData != nullptr) if (other.allocatedData != nullptr)
{ {
allocatedData.malloc (size); allocatedData.malloc ((size_t) size);
memcpy (allocatedData, other.allocatedData, (size_t) size); memcpy (allocatedData, other.allocatedData, (size_t) size);
} }
else else
@@ -143,7 +143,7 @@ MidiMessage::MidiMessage (const MidiMessage& other, const double newTimeStamp)
{ {
if (other.allocatedData != nullptr) if (other.allocatedData != nullptr)
{ {
allocatedData.malloc (size); allocatedData.malloc ((size_t) size);
memcpy (allocatedData, other.allocatedData, (size_t) size); memcpy (allocatedData, other.allocatedData, (size_t) size);
} }
else else
@@ -255,7 +255,7 @@ MidiMessage& MidiMessage::operator= (const MidiMessage& other)
if (other.allocatedData != nullptr) if (other.allocatedData != nullptr)
{ {
allocatedData.malloc (size); allocatedData.malloc ((size_t) size);
memcpy (allocatedData, other.allocatedData, (size_t) size); memcpy (allocatedData, other.allocatedData, (size_t) size);
} }
else else
@@ -297,7 +297,7 @@ uint8* MidiMessage::allocateSpace (int bytes)
{ {
if (bytes > 4) if (bytes > 4)
{ {
allocatedData.malloc (bytes); allocatedData.malloc ((size_t) bytes);
return allocatedData; return allocatedData;
} }
@@ -661,7 +661,7 @@ String MidiMessage::getTextFromTextMetaEvent() const
MidiMessage MidiMessage::textMetaEvent (int type, StringRef text) MidiMessage MidiMessage::textMetaEvent (int type, StringRef text)
{ {
jassert (type > 0 && type < 16) jassert (type > 0 && type < 16);
MidiMessage result; MidiMessage result;
@@ -29,6 +29,7 @@ MidiMessageSequence::MidiMessageSequence()
MidiMessageSequence::MidiMessageSequence (const MidiMessageSequence& other) MidiMessageSequence::MidiMessageSequence (const MidiMessageSequence& other)
{ {
list.addCopiesOf (other.list); list.addCopiesOf (other.list);
updateMatchedPairs();
} }
MidiMessageSequence& MidiMessageSequence::operator= (const MidiMessageSequence& other) MidiMessageSequence& MidiMessageSequence::operator= (const MidiMessageSequence& other)
@@ -52,17 +53,17 @@ void MidiMessageSequence::clear()
list.clear(); list.clear();
} }
int MidiMessageSequence::getNumEvents() const int MidiMessageSequence::getNumEvents() const noexcept
{ {
return list.size(); return list.size();
} }
MidiMessageSequence::MidiEventHolder* MidiMessageSequence::getEventPointer (const int index) const MidiMessageSequence::MidiEventHolder* MidiMessageSequence::getEventPointer (const int index) const noexcept
{ {
return list [index]; return list [index];
} }
double MidiMessageSequence::getTimeOfMatchingKeyUp (const int index) const double MidiMessageSequence::getTimeOfMatchingKeyUp (const int index) const noexcept
{ {
if (const MidiEventHolder* const meh = list [index]) if (const MidiEventHolder* const meh = list [index])
if (meh->noteOffObject != nullptr) if (meh->noteOffObject != nullptr)
@@ -71,7 +72,7 @@ double MidiMessageSequence::getTimeOfMatchingKeyUp (const int index) const
return 0.0; return 0.0;
} }
int MidiMessageSequence::getIndexOfMatchingKeyUp (const int index) const int MidiMessageSequence::getIndexOfMatchingKeyUp (const int index) const noexcept
{ {
if (const MidiEventHolder* const meh = list [index]) if (const MidiEventHolder* const meh = list [index])
return list.indexOf (meh->noteOffObject); return list.indexOf (meh->noteOffObject);
@@ -79,12 +80,12 @@ int MidiMessageSequence::getIndexOfMatchingKeyUp (const int index) const
return -1; return -1;
} }
int MidiMessageSequence::getIndexOf (MidiEventHolder* const event) const int MidiMessageSequence::getIndexOf (MidiEventHolder* const event) const noexcept
{ {
return list.indexOf (event); return list.indexOf (event);
} }
int MidiMessageSequence::getNextIndexAtTime (const double timeStamp) const int MidiMessageSequence::getNextIndexAtTime (const double timeStamp) const noexcept
{ {
const int numEvents = list.size(); const int numEvents = list.size();
@@ -97,17 +98,17 @@ int MidiMessageSequence::getNextIndexAtTime (const double timeStamp) const
} }
//============================================================================== //==============================================================================
double MidiMessageSequence::getStartTime() const double MidiMessageSequence::getStartTime() const noexcept
{ {
return getEventTime (0); return getEventTime (0);
} }
double MidiMessageSequence::getEndTime() const double MidiMessageSequence::getEndTime() const noexcept
{ {
return getEventTime (list.size() - 1); return getEventTime (list.size() - 1);
} }
double MidiMessageSequence::getEventTime (const int index) const double MidiMessageSequence::getEventTime (const int index) const noexcept
{ {
if (const MidiEventHolder* const meh = list [index]) if (const MidiEventHolder* const meh = list [index])
return meh->message.getTimeStamp(); return meh->message.getTimeStamp();
@@ -181,13 +182,13 @@ void MidiMessageSequence::addSequence (const MidiMessageSequence& other,
} }
//============================================================================== //==============================================================================
void MidiMessageSequence::sort() void MidiMessageSequence::sort() noexcept
{ {
MidiMessageSequenceSorter sorter; MidiMessageSequenceSorter sorter;
list.sort (sorter, true); list.sort (sorter, true);
} }
void MidiMessageSequence::updateMatchedPairs() void MidiMessageSequence::updateMatchedPairs() noexcept
{ {
for (int i = 0; i < list.size(); ++i) for (int i = 0; i < list.size(); ++i)
{ {
@@ -226,7 +227,7 @@ void MidiMessageSequence::updateMatchedPairs()
} }
} }
void MidiMessageSequence::addTimeToMessages (const double delta) void MidiMessageSequence::addTimeToMessages (const double delta) noexcept
{ {
for (int i = list.size(); --i >= 0;) for (int i = list.size(); --i >= 0;)
{ {
@@ -91,47 +91,47 @@ public:
void clear(); void clear();
/** Returns the number of events in the sequence. */ /** Returns the number of events in the sequence. */
int getNumEvents() const; int getNumEvents() const noexcept;
/** Returns a pointer to one of the events. */ /** Returns a pointer to one of the events. */
MidiEventHolder* getEventPointer (int index) const; MidiEventHolder* getEventPointer (int index) const noexcept;
/** Returns the time of the note-up that matches the note-on at this index. /** 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. If the event at this index isn't a note-on, it'll just return 0.
@see MidiMessageSequence::MidiEventHolder::noteOffObject @see MidiMessageSequence::MidiEventHolder::noteOffObject
*/ */
double getTimeOfMatchingKeyUp (int index) const; double getTimeOfMatchingKeyUp (int index) const noexcept;
/** Returns the index of the note-up that matches the note-on at this index. /** 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. If the event at this index isn't a note-on, it'll just return -1.
@see MidiMessageSequence::MidiEventHolder::noteOffObject @see MidiMessageSequence::MidiEventHolder::noteOffObject
*/ */
int getIndexOfMatchingKeyUp (int index) const; int getIndexOfMatchingKeyUp (int index) const noexcept;
/** Returns the index of an event. */ /** Returns the index of an event. */
int getIndexOf (MidiEventHolder* event) const; int getIndexOf (MidiEventHolder* event) const noexcept;
/** Returns the index of the first event on or after the given timestamp. /** 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 If the time is beyond the end of the sequence, this will return the
number of events. number of events.
*/ */
int getNextIndexAtTime (double timeStamp) const; int getNextIndexAtTime (double timeStamp) const noexcept;
//============================================================================== //==============================================================================
/** Returns the timestamp of the first event in the sequence. /** Returns the timestamp of the first event in the sequence.
@see getEndTime @see getEndTime
*/ */
double getStartTime() const; double getStartTime() const noexcept;
/** Returns the timestamp of the last event in the sequence. /** Returns the timestamp of the last event in the sequence.
@see getStartTime @see getStartTime
*/ */
double getEndTime() const; double getEndTime() const noexcept;
/** Returns the timestamp of the event at a given index. /** Returns the timestamp of the event at a given index.
If the index is out-of-range, this will return 0.0 If the index is out-of-range, this will return 0.0
*/ */
double getEventTime (int index) const; double getEventTime (int index) const noexcept;
//============================================================================== //==============================================================================
/** Inserts a midi message into the sequence. /** Inserts a midi message into the sequence.
@@ -185,13 +185,13 @@ public:
will scan the list and make sure all the note-offs in the MidiEventHolder will scan the list and make sure all the note-offs in the MidiEventHolder
structures are pointing at the correct ones. structures are pointing at the correct ones.
*/ */
void updateMatchedPairs(); void updateMatchedPairs() noexcept;
/** Forces a sort of the sequence. /** Forces a sort of the sequence.
You may need to call this if you've manually modified the timestamps of some You may need to call this if you've manually modified the timestamps of some
events such that the overall order now needs updating. events such that the overall order now needs updating.
*/ */
void sort(); void sort() noexcept;
//============================================================================== //==============================================================================
/** Copies all the messages for a particular midi channel to another sequence. /** Copies all the messages for a particular midi channel to another sequence.
@@ -224,7 +224,7 @@ public:
/** Adds an offset to the timestamps of all events in the sequence. /** Adds an offset to the timestamps of all events in the sequence.
@param deltaTime the amount to add to each timestamp. @param deltaTime the amount to add to each timestamp.
*/ */
void addTimeToMessages (double deltaTime); void addTimeToMessages (double deltaTime) noexcept;
//============================================================================== //==============================================================================
/** Scans through the sequence to determine the state of any midi controllers at /** Scans through the sequence to determine the state of any midi controllers at
@@ -47,23 +47,28 @@ void ResamplingAudioSource::setResamplingRatio (const double samplesInPerOutputS
ratio = jmax (0.0, samplesInPerOutputSample); ratio = jmax (0.0, samplesInPerOutputSample);
} }
void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected, void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate)
double sampleRate)
{ {
const SpinLock::ScopedLockType sl (ratioLock); const SpinLock::ScopedLockType sl (ratioLock);
input->prepareToPlay (samplesPerBlockExpected, sampleRate); input->prepareToPlay (samplesPerBlockExpected, sampleRate);
buffer.setSize (numChannels, roundToInt (samplesPerBlockExpected * ratio) + 32); buffer.setSize (numChannels, roundToInt (samplesPerBlockExpected * ratio) + 32);
buffer.clear();
sampsInBuffer = 0;
bufferPos = 0;
subSampleOffset = 0.0;
filterStates.calloc ((size_t) numChannels); filterStates.calloc ((size_t) numChannels);
srcBuffers.calloc ((size_t) numChannels); srcBuffers.calloc ((size_t) numChannels);
destBuffers.calloc ((size_t) numChannels); destBuffers.calloc ((size_t) numChannels);
createLowPass (ratio); createLowPass (ratio);
flushBuffers();
}
void ResamplingAudioSource::flushBuffers()
{
buffer.clear();
bufferPos = 0;
sampsInBuffer = 0;
subSampleOffset = 0.0;
resetFilters(); resetFilters();
} }
@@ -225,7 +230,8 @@ void ResamplingAudioSource::setFilterCoefficients (double c1, double c2, double
void ResamplingAudioSource::resetFilters() void ResamplingAudioSource::resetFilters()
{ {
filterStates.clear ((size_t) numChannels); if (filterStates != nullptr)
filterStates.clear ((size_t) numChannels);
} }
void ResamplingAudioSource::applyFilter (float* samples, int num, FilterState& fs) void ResamplingAudioSource::applyFilter (float* samples, int num, FilterState& fs)
@@ -66,6 +66,9 @@ public:
*/ */
double getResamplingRatio() const noexcept { return ratio; } double getResamplingRatio() const noexcept { return ratio; }
/** Clears any buffers and filters that the resampler is using. */
void flushBuffers();
//============================================================================== //==============================================================================
void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override;
void releaseResources() override; void releaseResources() override;
@@ -163,10 +163,7 @@ void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBu
: numSamples; : numSamples;
if (numThisTime > 0) if (numThisTime > 0)
{ renderVoices (outputBuffer, startSample, numThisTime);
for (int i = voices.size(); --i >= 0;)
voices.getUnchecked (i)->renderNextBlock (outputBuffer, startSample, numThisTime);
}
if (useEvent) if (useEvent)
handleMidiEvent (m); handleMidiEvent (m);
@@ -176,6 +173,12 @@ void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBu
} }
} }
void Synthesiser::renderVoices (AudioSampleBuffer& buffer, int startSample, int numSamples)
{
for (int i = voices.size(); --i >= 0;)
voices.getUnchecked (i)->renderNextBlock (buffer, startSample, numSamples);
}
void Synthesiser::handleMidiEvent (const MidiMessage& m) void Synthesiser::handleMidiEvent (const MidiMessage& m)
{ {
if (m.isNoteOn()) if (m.isNoteOn())
@@ -184,7 +187,7 @@ void Synthesiser::handleMidiEvent (const MidiMessage& m)
} }
else if (m.isNoteOff()) else if (m.isNoteOff())
{ {
noteOff (m.getChannel(), m.getNoteNumber(), true); noteOff (m.getChannel(), m.getNoteNumber(), m.getFloatVelocity(), true);
} }
else if (m.isAllNotesOff() || m.isAllSoundOff()) else if (m.isAllNotesOff() || m.isAllSoundOff())
{ {
@@ -230,10 +233,10 @@ void Synthesiser::noteOn (const int midiChannel,
if (voice->getCurrentlyPlayingNote() == midiNoteNumber if (voice->getCurrentlyPlayingNote() == midiNoteNumber
&& voice->isPlayingChannel (midiChannel)) && voice->isPlayingChannel (midiChannel))
stopVoice (voice, true); stopVoice (voice, 1.0f, true);
} }
startVoice (findFreeVoice (sound, shouldStealNotes), startVoice (findFreeVoice (sound, midiChannel, midiNoteNumber, shouldStealNotes),
sound, midiChannel, midiNoteNumber, velocity); sound, midiChannel, midiNoteNumber, velocity);
} }
} }
@@ -248,7 +251,7 @@ void Synthesiser::startVoice (SynthesiserVoice* const voice,
if (voice != nullptr && sound != nullptr) if (voice != nullptr && sound != nullptr)
{ {
if (voice->currentlyPlayingSound != nullptr) if (voice->currentlyPlayingSound != nullptr)
voice->stopNote (false); voice->stopNote (0.0f, false);
voice->startNote (midiNoteNumber, velocity, sound, voice->startNote (midiNoteNumber, velocity, sound,
lastPitchWheelValues [midiChannel - 1]); lastPitchWheelValues [midiChannel - 1]);
@@ -261,11 +264,11 @@ void Synthesiser::startVoice (SynthesiserVoice* const voice,
} }
} }
void Synthesiser::stopVoice (SynthesiserVoice* voice, const bool allowTailOff) void Synthesiser::stopVoice (SynthesiserVoice* voice, float velocity, const bool allowTailOff)
{ {
jassert (voice != nullptr); jassert (voice != nullptr);
voice->stopNote (allowTailOff); voice->stopNote (velocity, allowTailOff);
// the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()! // the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()!
jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == 0)); jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == 0));
@@ -273,6 +276,7 @@ void Synthesiser::stopVoice (SynthesiserVoice* voice, const bool allowTailOff)
void Synthesiser::noteOff (const int midiChannel, void Synthesiser::noteOff (const int midiChannel,
const int midiNoteNumber, const int midiNoteNumber,
const float velocity,
const bool allowTailOff) const bool allowTailOff)
{ {
const ScopedLock sl (lock); const ScopedLock sl (lock);
@@ -291,7 +295,7 @@ void Synthesiser::noteOff (const int midiChannel,
voice->keyIsDown = false; voice->keyIsDown = false;
if (! (sustainPedalsDown [midiChannel] || voice->sostenutoPedalDown)) if (! (sustainPedalsDown [midiChannel] || voice->sostenutoPedalDown))
stopVoice (voice, allowTailOff); stopVoice (voice, velocity, allowTailOff);
} }
} }
} }
@@ -307,7 +311,7 @@ void Synthesiser::allNotesOff (const int midiChannel, const bool allowTailOff)
SynthesiserVoice* const voice = voices.getUnchecked (i); SynthesiserVoice* const voice = voices.getUnchecked (i);
if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)) if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
voice->stopNote (allowTailOff); voice->stopNote (1.0f, allowTailOff);
} }
sustainPedalsDown.clear(); sustainPedalsDown.clear();
@@ -379,7 +383,7 @@ void Synthesiser::handleSustainPedal (int midiChannel, bool isDown)
SynthesiserVoice* const voice = voices.getUnchecked (i); SynthesiserVoice* const voice = voices.getUnchecked (i);
if (voice->isPlayingChannel (midiChannel) && ! voice->keyIsDown) if (voice->isPlayingChannel (midiChannel) && ! voice->keyIsDown)
stopVoice (voice, true); stopVoice (voice, 1.0f, true);
} }
sustainPedalsDown.clearBit (midiChannel); sustainPedalsDown.clearBit (midiChannel);
@@ -400,7 +404,7 @@ void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown)
if (isDown) if (isDown)
voice->sostenutoPedalDown = true; voice->sostenutoPedalDown = true;
else if (voice->sostenutoPedalDown) else if (voice->sostenutoPedalDown)
stopVoice (voice, true); stopVoice (voice, 1.0f, true);
} }
} }
} }
@@ -412,7 +416,9 @@ void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/)
} }
//============================================================================== //==============================================================================
SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, const bool stealIfNoneAvailable) const SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay,
int midiChannel, int midiNoteNumber,
const bool stealIfNoneAvailable) const
{ {
const ScopedLock sl (lock); const ScopedLock sl (lock);
@@ -425,25 +431,68 @@ SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, con
} }
if (stealIfNoneAvailable) if (stealIfNoneAvailable)
return findVoiceToSteal (soundToPlay); return findVoiceToSteal (soundToPlay, midiChannel, midiNoteNumber);
return nullptr; return nullptr;
} }
SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay) const struct VoiceAgeSorter
{ {
// currently this just steals the one that's been playing the longest, but could be made a bit smarter.. static int compareElements (SynthesiserVoice* v1, SynthesiserVoice* v2) noexcept
SynthesiserVoice* oldest = nullptr; {
return v1->wasStartedBefore (*v2) ? 1 : (v2->wasStartedBefore (*v1) ? -1 : 0);
}
};
SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay,
int /*midiChannel*/, int midiNoteNumber) const
{
SynthesiserVoice* bottom = nullptr;
SynthesiserVoice* top = nullptr;
// this is a list of voices we can steal, sorted by how long they've been running
Array<SynthesiserVoice*> usableVoices;
usableVoices.ensureStorageAllocated (voices.size());
for (int i = 0; i < voices.size(); ++i) for (int i = 0; i < voices.size(); ++i)
{ {
SynthesiserVoice* const voice = voices.getUnchecked (i); SynthesiserVoice* const voice = voices.getUnchecked (i);
if (voice->canPlaySound (soundToPlay) if (voice->canPlaySound (soundToPlay))
&& (oldest == nullptr || voice->wasStartedBefore (*oldest))) {
oldest = voice; VoiceAgeSorter sorter;
usableVoices.addSorted (sorter, voice);
const int note = voice->getCurrentlyPlayingNote();
if (bottom == nullptr || note < bottom->getCurrentlyPlayingNote())
bottom = voice;
if (top == nullptr || note > top->getCurrentlyPlayingNote())
top = voice;
}
} }
jassert (oldest != nullptr); jassert (bottom != nullptr && top != nullptr);
return oldest;
// The oldest note that's playing with the target pitch playing is ideal..
for (int i = 0; i < usableVoices.size(); ++i)
{
SynthesiserVoice* const voice = usableVoices.getUnchecked (i);
if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
return voice;
}
// ..otherwise, look for the oldest note that isn't the top or bottom note..
for (int i = 0; i < usableVoices.size(); ++i)
{
SynthesiserVoice* const voice = usableVoices.getUnchecked (i);
if (voice != bottom && voice != top)
return voice;
}
// ..otherwise, there's only one or two voices to choose from - we'll return the top one..
return top;
} }
@@ -55,14 +55,14 @@ public:
The Synthesiser will use this information when deciding which sounds to trigger The Synthesiser will use this information when deciding which sounds to trigger
for a given note. for a given note.
*/ */
virtual bool appliesToNote (const int midiNoteNumber) = 0; virtual bool appliesToNote (int midiNoteNumber) = 0;
/** Returns true if the sound should be triggered by midi events on a given channel. /** Returns true if the sound should be triggered by midi events on a given channel.
The Synthesiser will use this information when deciding which sounds to trigger The Synthesiser will use this information when deciding which sounds to trigger
for a given note. for a given note.
*/ */
virtual bool appliesToChannel (const int midiChannel) = 0; virtual bool appliesToChannel (int midiChannel) = 0;
/** The class is reference-counted, so this is a handy pointer class for it. */ /** The class is reference-counted, so this is a handy pointer class for it. */
typedef ReferenceCountedObjectPtr<SynthesiserSound> Ptr; typedef ReferenceCountedObjectPtr<SynthesiserSound> Ptr;
@@ -127,6 +127,8 @@ public:
This will be called during the rendering callback, so must be fast and thread-safe. This will be called during the rendering callback, so must be fast and thread-safe.
The velocity indicates how quickly the note was released - 0 is slowly, 1 is quickly.
If allowTailOff is false or the voice doesn't want to tail-off, then it must stop all If allowTailOff is false or the voice doesn't want to tail-off, then it must stop all
sound immediately, and must call clearCurrentNote() to reset the state of this voice sound immediately, and must call clearCurrentNote() to reset the state of this voice
and allow the synth to reassign it another sound. and allow the synth to reassign it another sound.
@@ -136,7 +138,7 @@ public:
finishes playing (during the rendering callback), it must make sure that it calls finishes playing (during the rendering callback), it must make sure that it calls
clearCurrentNote(). clearCurrentNote().
*/ */
virtual void stopNote (bool allowTailOff) = 0; virtual void stopNote (float velocity, bool allowTailOff) = 0;
/** Called to let the voice know that the pitch wheel has been moved. /** 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. This will be called during the rendering callback, so must be fast and thread-safe.
@@ -173,13 +175,6 @@ public:
int startSample, int startSample,
int numSamples) = 0; int numSamples) = 0;
/** 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;
/** Changes the voice's reference sample rate. /** Changes the voice's reference sample rate.
The rate is set so that subclasses know the output rate and can set their pitch The rate is set so that subclasses know the output rate and can set their pitch
@@ -188,7 +183,19 @@ public:
This method is called by the synth, and subclasses can access the current rate with This method is called by the synth, and subclasses can access the current rate with
the currentSampleRate member. the currentSampleRate member.
*/ */
void setCurrentPlaybackSampleRate (double newRate); 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;
/** Returns true if the key that triggered this voice is still held down. /** 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 Note that the voice may still be playing after the key was released (e.g because the
@@ -203,13 +210,6 @@ public:
bool wasStartedBefore (const SynthesiserVoice& other) const noexcept; bool wasStartedBefore (const SynthesiserVoice& other) const noexcept;
protected: protected:
//==============================================================================
/** Returns the current target sample rate at which rendering is being done.
This is available for subclasses so they can pitch things correctly.
*/
double getSampleRate() const { return currentSampleRate; }
/** Resets the state of this voice after a sound has finished playing. /** Resets the state of this voice after a sound has finished playing.
The subclass must call this when it finishes playing a note and becomes available The subclass must call this when it finishes playing a note and becomes available
@@ -235,6 +235,11 @@ private:
SynthesiserSound::Ptr currentlyPlayingSound; SynthesiserSound::Ptr currentlyPlayingSound;
bool keyIsDown, sostenutoPedalDown; bool keyIsDown, sostenutoPedalDown;
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
// Note the new parameters for this method.
virtual int stopNote (bool) { return 0; }
#endif
JUCE_LEAK_DETECTOR (SynthesiserVoice) JUCE_LEAK_DETECTOR (SynthesiserVoice)
}; };
@@ -268,8 +273,7 @@ class JUCE_API Synthesiser
public: public:
//============================================================================== //==============================================================================
/** Creates a new synthesiser. /** Creates a new synthesiser.
You'll need to add some sounds and voices before it'll make any sound.
You'll need to add some sounds and voices before it'll make any sound..
*/ */
Synthesiser(); Synthesiser();
@@ -365,6 +369,7 @@ public:
*/ */
virtual void noteOff (int midiChannel, virtual void noteOff (int midiChannel,
int midiNoteNumber, int midiNoteNumber,
float velocity,
bool allowTailOff); bool allowTailOff);
/** Turns off all notes. /** Turns off all notes.
@@ -444,7 +449,7 @@ public:
This value is propagated to the voices so that they can use it to render the correct This value is propagated to the voices so that they can use it to render the correct
pitches. pitches.
*/ */
void setCurrentPlaybackSampleRate (double sampleRate); virtual void setCurrentPlaybackSampleRate (double sampleRate);
/** Creates the next block of audio output. /** Creates the next block of audio output.
@@ -463,6 +468,11 @@ public:
int startSample, int startSample,
int numSamples); int numSamples);
/** 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 sampleRate; }
protected: protected:
//============================================================================== //==============================================================================
/** This is used to control access to the rendering callback and the note trigger methods. */ /** This is used to control access to the rendering callback and the note trigger methods. */
@@ -474,21 +484,34 @@ protected:
/** The last pitch-wheel values for each midi channel. */ /** The last pitch-wheel values for each midi channel. */
int lastPitchWheelValues [16]; int lastPitchWheelValues [16];
/** Searches through the voices to find one that's not currently playing, and which /** Renders the voices for the given range.
can play the given sound. By default this just calls renderNextBlock() on each voice, but you may need
to override it to handle custom cases.
*/
virtual void renderVoices (AudioSampleBuffer& outputAudio,
int startSample, int numSamples);
/** Searches through the voices to find one that's not currently playing, and
which can play the given sound.
Returns nullptr if all voices are busy and stealing isn't enabled. Returns nullptr if all voices are busy and stealing isn't enabled.
This can be overridden to implement custom voice-stealing algorithms. To implement a custom note-stealing algorithm, you can either override this
method, or (preferably) override findVoiceToSteal().
*/ */
virtual SynthesiserVoice* findFreeVoice (SynthesiserSound* soundToPlay, virtual SynthesiserVoice* findFreeVoice (SynthesiserSound* soundToPlay,
const bool stealIfNoneAvailable) const; int midiChannel,
int midiNoteNumber,
bool stealIfNoneAvailable) const;
/** Chooses a voice that is most suitable for being re-used. /** Chooses a voice that is most suitable for being re-used.
The default method returns the one that has been playing for the longest, but The default method will attempt to find the oldest voice that isn't the
you may want to override this and do something more cunning instead. bottom or top note being played. If that's not suitable for your synth,
you can override this method and do something more cunning instead.
*/ */
virtual SynthesiserVoice* findVoiceToSteal (SynthesiserSound* soundToPlay) const; virtual SynthesiserVoice* findVoiceToSteal (SynthesiserSound* soundToPlay,
int midiChannel,
int midiNoteNumber) const;
/** Starts a specified voice playing a particular sound. /** Starts a specified voice playing a particular sound.
@@ -511,11 +534,14 @@ private:
bool shouldStealNotes; bool shouldStealNotes;
BigInteger sustainPedalsDown; BigInteger sustainPedalsDown;
void stopVoice (SynthesiserVoice*, bool allowTailOff); void stopVoice (SynthesiserVoice*, float velocity, bool allowTailOff);
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE #if JUCE_CATCH_DEPRECATED_CODE_MISUSE
// Note the new parameters for this method. // Note the new parameters for these methods.
virtual int findFreeVoice (const bool) const { return 0; } virtual int findFreeVoice (const bool) const { return 0; }
virtual int noteOff (int, int, int) { return 0; }
virtual int findFreeVoice (SynthesiserSound*, const bool) { return 0; }
virtual int findVoiceToSteal (SynthesiserSound*) const { return 0; }
#endif #endif
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Synthesiser) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Synthesiser)
@@ -859,8 +859,11 @@ void AudioDeviceManager::addMidiInputCallback (const String& name, MidiInputCall
if (name.isEmpty() || isMidiInputEnabled (name)) if (name.isEmpty() || isMidiInputEnabled (name))
{ {
const ScopedLock sl (midiCallbackLock); const ScopedLock sl (midiCallbackLock);
midiCallbacks.add (callbackToAdd);
midiCallbackDevices.add (name); MidiCallbackInfo mc;
mc.deviceName = name;
mc.callback = callbackToAdd;
midiCallbacks.add (mc);
} }
} }
@@ -868,11 +871,12 @@ void AudioDeviceManager::removeMidiInputCallback (const String& name, MidiInputC
{ {
for (int i = midiCallbacks.size(); --i >= 0;) for (int i = midiCallbacks.size(); --i >= 0;)
{ {
if (midiCallbackDevices[i] == name && midiCallbacks.getUnchecked(i) == callbackToRemove) const MidiCallbackInfo& mc = midiCallbacks.getReference(i);
if (mc.callback == callbackToRemove && mc.deviceName == name)
{ {
const ScopedLock sl (midiCallbackLock); const ScopedLock sl (midiCallbackLock);
midiCallbacks.remove (i); midiCallbacks.remove (i);
midiCallbackDevices.remove (i);
} }
} }
} }
@@ -881,16 +885,14 @@ void AudioDeviceManager::handleIncomingMidiMessageInt (MidiInput* source, const
{ {
if (! message.isActiveSense()) if (! message.isActiveSense())
{ {
const bool isDefaultSource = (source == nullptr || source == enabledMidiInputs.getFirst());
const ScopedLock sl (midiCallbackLock); const ScopedLock sl (midiCallbackLock);
for (int i = midiCallbackDevices.size(); --i >= 0;) for (int i = 0; i < midiCallbacks.size(); ++i)
{ {
const String name (midiCallbackDevices[i]); const MidiCallbackInfo& mc = midiCallbacks.getReference(i);
if ((isDefaultSource && name.isEmpty()) || (name.isNotEmpty() && name == source->getName())) if (mc.deviceName.isEmpty() || mc.deviceName == source->getName())
midiCallbacks.getUnchecked(i)->handleIncomingMidiMessage (source, message); mc.callback->handleIncomingMidiMessage (source, message);
} }
} }
} }
@@ -209,10 +209,9 @@ public:
//============================================================================== //==============================================================================
/** Returns the current device properties that are in use. /** Returns the current device properties that are in use.
@see setAudioDeviceSetup @see setAudioDeviceSetup
*/ */
void getAudioDeviceSetup (AudioDeviceSetup& setup); void getAudioDeviceSetup (AudioDeviceSetup& result);
/** Changes the current device or its settings. /** Changes the current device or its settings.
@@ -261,9 +260,7 @@ public:
void setCurrentAudioDeviceType (const String& type, void setCurrentAudioDeviceType (const String& type,
bool treatAsChosenDevice); bool treatAsChosenDevice);
/** Closes the currently-open device. /** Closes the currently-open device.
You can call restartLastAudioDevice() later to reopen it in the same state You can call restartLastAudioDevice() later to reopen it in the same state
that it was just in. that it was just in.
*/ */
@@ -305,8 +302,8 @@ public:
//============================================================================== //==============================================================================
/** Returns the average proportion of available CPU being spent inside the audio callbacks. /** Returns the average proportion of available CPU being spent inside the audio callbacks.
@returns A value between 0 and 1.0 to indicate the approximate proportion of CPU
Returns a value between 0 and 1.0 time spent in the callbacks.
*/ */
double getCpuUsage() const; double getCpuUsage() const;
@@ -333,16 +330,16 @@ public:
void setMidiInputEnabled (const String& midiInputDeviceName, bool enabled); void setMidiInputEnabled (const String& midiInputDeviceName, bool enabled);
/** Returns true if a given midi input device is being used. /** Returns true if a given midi input device is being used.
@see setMidiInputEnabled @see setMidiInputEnabled
*/ */
bool isMidiInputEnabled (const String& midiInputDeviceName) const; bool isMidiInputEnabled (const String& midiInputDeviceName) const;
/** Registers a listener for callbacks when midi events arrive from a midi input. /** Registers a listener for callbacks when midi events arrive from a midi input.
The device name can be empty to indicate that it wants events from whatever the The device name can be empty to indicate that it wants to receive all incoming
current "default" device is. Or it can be the name of one of the midi input devices events from all the enabled MIDI inputs. Or it can be the name of one of the
(see MidiInput::getDevices() for the names). MIDI input devices if it just wants the events from that device. (see
MidiInput::getDevices() for the list of device names).
Only devices which are enabled (see the setMidiInputEnabled() method) will have their Only devices which are enabled (see the setMidiInputEnabled() method) will have their
events forwarded on to listeners. events forwarded on to listeners.
@@ -350,8 +347,7 @@ public:
void addMidiInputCallback (const String& midiInputDeviceName, void addMidiInputCallback (const String& midiInputDeviceName,
MidiInputCallback* callback); MidiInputCallback* callback);
/** Removes a listener that was previously registered with addMidiInputCallback(). /** Removes a listener that was previously registered with addMidiInputCallback(). */
*/
void removeMidiInputCallback (const String& midiInputDeviceName, void removeMidiInputCallback (const String& midiInputDeviceName,
MidiInputCallback* callback); MidiInputCallback* callback);
@@ -371,22 +367,17 @@ public:
void setDefaultMidiOutput (const String& deviceName); void setDefaultMidiOutput (const String& deviceName);
/** Returns the name of the default midi output. /** Returns the name of the default midi output.
@see setDefaultMidiOutput, getDefaultMidiOutput @see setDefaultMidiOutput, getDefaultMidiOutput
*/ */
String getDefaultMidiOutputName() const { return defaultMidiOutputName; } const String& getDefaultMidiOutputName() const noexcept { return defaultMidiOutputName; }
/** Returns the current default midi output device. /** Returns the current default midi output device.
If no device has been selected, or the device can't be opened, this will return nullptr.
If no device has been selected, or the device can't be opened, this will
return 0.
@see getDefaultMidiOutputName @see getDefaultMidiOutputName
*/ */
MidiOutput* getDefaultMidiOutput() const noexcept { return defaultMidiOutput; } MidiOutput* getDefaultMidiOutput() const noexcept { return defaultMidiOutput; }
/** Returns a list of the types of device supported. /** Returns a list of the types of device supported. */
*/
const OwnedArray<AudioIODeviceType>& getAvailableDeviceTypes(); const OwnedArray<AudioIODeviceType>& getAvailableDeviceTypes();
//============================================================================== //==============================================================================
@@ -429,9 +420,7 @@ public:
void enableInputLevelMeasurement (bool enableMeasurement); void enableInputLevelMeasurement (bool enableMeasurement);
/** Returns the current input level. /** Returns the current input level.
To use this, you must first enable it by calling enableInputLevelMeasurement(). To use this, you must first enable it by calling enableInputLevelMeasurement().
See enableInputLevelMeasurement() for more info. See enableInputLevelMeasurement() for more info.
*/ */
double getCurrentInputLevel() const; double getCurrentInputLevel() const;
@@ -468,10 +457,16 @@ private:
int testSoundPosition; int testSoundPosition;
AudioSampleBuffer tempBuffer; AudioSampleBuffer tempBuffer;
struct MidiCallbackInfo
{
String deviceName;
MidiInputCallback* callback;
};
StringArray midiInsFromXml; StringArray midiInsFromXml;
OwnedArray<MidiInput> enabledMidiInputs; OwnedArray<MidiInput> enabledMidiInputs;
Array<MidiInputCallback*> midiCallbacks; Array<MidiCallbackInfo> midiCallbacks;
StringArray midiCallbackDevices;
String defaultMidiOutputName; String defaultMidiOutputName;
ScopedPointer<MidiOutput> defaultMidiOutput; ScopedPointer<MidiOutput> defaultMidiOutput;
CriticalSection audioCallbackLock, midiCallbackLock; CriticalSection audioCallbackLock, midiCallbackLock;
@@ -22,20 +22,16 @@
============================================================================== ==============================================================================
*/ */
AudioIODevice::AudioIODevice (const String& deviceName, const String& typeName_) AudioIODevice::AudioIODevice (const String& deviceName, const String& deviceTypeName)
: name (deviceName), : name (deviceName), typeName (deviceTypeName)
typeName (typeName_)
{ {
} }
AudioIODevice::~AudioIODevice() AudioIODevice::~AudioIODevice() {}
{
}
bool AudioIODevice::hasControlPanel() const void AudioIODeviceCallback::audioDeviceError (const String&) {}
{ bool AudioIODevice::setAudioPreprocessingEnabled (bool) { return false; }
return false; bool AudioIODevice::hasControlPanel() const { return false; }
}
bool AudioIODevice::showControlPanel() bool AudioIODevice::showControlPanel()
{ {
@@ -43,6 +39,3 @@ bool AudioIODevice::showControlPanel()
// their hasControlPanel() method. // their hasControlPanel() method.
return false; return false;
} }
//==============================================================================
void AudioIODeviceCallback::audioDeviceError (const String&) {}
@@ -289,6 +289,11 @@ public:
*/ */
virtual bool showControlPanel(); virtual bool showControlPanel();
/** On devices which support it, this allows automatic gain control or other
mic processing to be disabled.
If the device doesn't support this operation, it'll return false.
*/
virtual bool setAudioPreprocessingEnabled (bool shouldBeEnabled);
//============================================================================== //==============================================================================
protected: protected:
@@ -34,7 +34,7 @@
method. Each of the objects returned can then be used to list the available method. Each of the objects returned can then be used to list the available
devices of that type. E.g. devices of that type. E.g.
@code @code
OwnedArray <AudioIODeviceType> types; OwnedArray<AudioIODeviceType> types;
myAudioDeviceManager.createAudioDeviceTypes (types); myAudioDeviceManager.createAudioDeviceTypes (types);
for (int i = 0; i < types.size(); ++i) for (int i = 0; i < types.size(); ++i)
@@ -125,6 +125,7 @@
#if JUCE_USE_ANDROID_OPENSLES #if JUCE_USE_ANDROID_OPENSLES
#include <SLES/OpenSLES.h> #include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h> #include <SLES/OpenSLES_Android.h>
#include <SLES/OpenSLES_AndroidConfiguration.h>
#endif #endif
#endif #endif
@@ -1,7 +1,7 @@
{ {
"id": "juce_audio_devices", "id": "juce_audio_devices",
"name": "JUCE audio and midi I/O device classes", "name": "JUCE audio and midi I/O device classes",
"version": "3.0.5", "version": "3.0.8",
"description": "Classes to play and record from audio and midi i/o devices.", "description": "Classes to play and record from audio and midi i/o devices.",
"website": "http://www.juce.com/juce", "website": "http://www.juce.com/juce",
"license": "GPL/Commercial", "license": "GPL/Commercial",
@@ -80,7 +80,7 @@ public:
Array<double> getAvailableSampleRates() override Array<double> getAvailableSampleRates() override
{ {
static const double rates[] = { 8000.0, 16000.0, 32000.0, 44100.0, 48000.0 }; static const double rates[] = { 8000.0, 16000.0, 32000.0, 44100.0, 48000.0 };
return Array<double> (rates, numElementsInArray (rates)); return Array<double> (rates, numElementsInArray (rates));
} }
@@ -165,6 +165,33 @@ public:
oldCallback->audioDeviceStopped(); oldCallback->audioDeviceStopped();
} }
bool setAudioPreprocessingEnabled (bool enable) override
{
return recorder != nullptr && recorder->setAudioPreprocessingEnabled (enable);
}
private:
//==================================================================================================
CriticalSection callbackLock;
AudioIODeviceCallback* callback;
int actualBufferSize, sampleRate;
int inputLatency, outputLatency;
bool deviceOpen;
String lastError;
BigInteger activeOutputChans, activeInputChans;
int numInputChannels, numOutputChannels;
AudioSampleBuffer inputBuffer, outputBuffer;
struct Player;
struct Recorder;
AudioIODeviceCallback* setCallback (AudioIODeviceCallback* const newCallback)
{
const ScopedLock sl (callbackLock);
AudioIODeviceCallback* const oldCallback = callback;
callback = newCallback;
return oldCallback;
}
void run() override void run() override
{ {
if (recorder != nullptr) recorder->start(); if (recorder != nullptr) recorder->start();
@@ -190,28 +217,6 @@ public:
} }
} }
private:
//==================================================================================================
CriticalSection callbackLock;
AudioIODeviceCallback* callback;
int actualBufferSize, sampleRate;
int inputLatency, outputLatency;
bool deviceOpen;
String lastError;
BigInteger activeOutputChans, activeInputChans;
int numInputChannels, numOutputChannels;
AudioSampleBuffer inputBuffer, outputBuffer;
struct Player;
struct Recorder;
AudioIODeviceCallback* setCallback (AudioIODeviceCallback* const newCallback)
{
const ScopedLock sl (callbackLock);
AudioIODeviceCallback* const oldCallback = callback;
callback = newCallback;
return oldCallback;
}
//================================================================================================== //==================================================================================================
struct Engine struct Engine
{ {
@@ -230,6 +235,7 @@ private:
SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (SLInterfaceID*) library.getFunction ("SL_IID_ANDROIDSIMPLEBUFFERQUEUE"); SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (SLInterfaceID*) library.getFunction ("SL_IID_ANDROIDSIMPLEBUFFERQUEUE");
SL_IID_PLAY = (SLInterfaceID*) library.getFunction ("SL_IID_PLAY"); SL_IID_PLAY = (SLInterfaceID*) library.getFunction ("SL_IID_PLAY");
SL_IID_RECORD = (SLInterfaceID*) library.getFunction ("SL_IID_RECORD"); SL_IID_RECORD = (SLInterfaceID*) library.getFunction ("SL_IID_RECORD");
SL_IID_ANDROIDCONFIGURATION = (SLInterfaceID*) library.getFunction ("SL_IID_ANDROIDCONFIGURATION");
check ((*engineObject)->Realize (engineObject, SL_BOOLEAN_FALSE)); check ((*engineObject)->Realize (engineObject, SL_BOOLEAN_FALSE));
check ((*engineObject)->GetInterface (engineObject, *SL_IID_ENGINE, &engineInterface)); check ((*engineObject)->GetInterface (engineObject, *SL_IID_ENGINE, &engineInterface));
@@ -271,6 +277,7 @@ private:
SLInterfaceID* SL_IID_ANDROIDSIMPLEBUFFERQUEUE; SLInterfaceID* SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
SLInterfaceID* SL_IID_PLAY; SLInterfaceID* SL_IID_PLAY;
SLInterfaceID* SL_IID_RECORD; SLInterfaceID* SL_IID_RECORD;
SLInterfaceID* SL_IID_ANDROIDCONFIGURATION;
private: private:
DynamicLibrary library; DynamicLibrary library;
@@ -334,8 +341,8 @@ private:
SLDataFormat_PCM pcmFormat = SLDataFormat_PCM pcmFormat =
{ {
SL_DATAFORMAT_PCM, SL_DATAFORMAT_PCM,
numChannels, (SLuint32) numChannels,
sampleRate * 1000, // (sample rate units are millihertz) (SLuint32) (sampleRate * 1000), // (sample rate units are millihertz)
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
@@ -434,7 +441,8 @@ private:
struct Recorder struct Recorder
{ {
Recorder (int numChannels, int sampleRate, Engine& engine) Recorder (int numChannels, int sampleRate, Engine& engine)
: recorderObject (nullptr), recorderRecord (nullptr), recorderBufferQueue (nullptr), : recorderObject (nullptr), recorderRecord (nullptr),
recorderBufferQueue (nullptr), configObject (nullptr),
bufferList (numChannels) bufferList (numChannels)
{ {
jassert (numChannels == 1); // STEREO doesn't always work!! jassert (numChannels == 1); // STEREO doesn't always work!!
@@ -442,8 +450,8 @@ private:
SLDataFormat_PCM pcmFormat = SLDataFormat_PCM pcmFormat =
{ {
SL_DATAFORMAT_PCM, SL_DATAFORMAT_PCM,
numChannels, (SLuint32) numChannels,
sampleRate * 1000, // (sample rate units are millihertz) (SLuint32) (sampleRate * 1000), // (sample rate units are millihertz)
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
(numChannels == 1) ? SL_SPEAKER_FRONT_CENTER : (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT), (numChannels == 1) ? SL_SPEAKER_FRONT_CENTER : (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT),
@@ -466,6 +474,7 @@ private:
{ {
check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_RECORD, &recorderRecord)); check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_RECORD, &recorderRecord));
check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue)); check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue));
check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_ANDROIDCONFIGURATION, &configObject));
check ((*recorderBufferQueue)->RegisterCallback (recorderBufferQueue, staticCallback, this)); check ((*recorderBufferQueue)->RegisterCallback (recorderBufferQueue, staticCallback, this));
check ((*recorderRecord)->SetRecordState (recorderRecord, SL_RECORDSTATE_STOPPED)); check ((*recorderRecord)->SetRecordState (recorderRecord, SL_RECORDSTATE_STOPPED));
@@ -532,10 +541,20 @@ private:
} }
} }
bool setAudioPreprocessingEnabled (bool enable)
{
SLuint32 mode = enable ? SL_ANDROID_RECORDING_PRESET_GENERIC
: SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
return configObject != nullptr
&& check ((*configObject)->SetConfiguration (configObject, SL_ANDROID_KEY_RECORDING_PRESET, &mode, sizeof (mode)));
}
private: private:
SLObjectItf recorderObject; SLObjectItf recorderObject;
SLRecordItf recorderRecord; SLRecordItf recorderRecord;
SLAndroidSimpleBufferQueueItf recorderBufferQueue; SLAndroidSimpleBufferQueueItf recorderBufferQueue;
SLAndroidConfigurationItf configObject;
BufferList bufferList; BufferList bufferList;
@@ -67,7 +67,12 @@ public:
return s; return s;
} }
Array<double> getAvailableSampleRates() override { return sampleRates; } Array<double> getAvailableSampleRates() override
{
// can't find a good way to actually ask the device for which of these it supports..
static const double rates[] = { 8000.0, 16000.0, 22050.0, 32000.0, 44100.0, 48000.0 };
return Array<double> (rates, numElementsInArray (rates));
}
Array<int> getAvailableBufferSizes() override Array<int> getAvailableBufferSizes() override
{ {
@@ -79,7 +84,7 @@ public:
return r; return r;
} }
int getDefaultBufferSize() override { return 1024; } int getDefaultBufferSize() override { return 1024; }
String open (const BigInteger& inputChannelsWanted, String open (const BigInteger& inputChannelsWanted,
const BigInteger& outputChannelsWanted, const BigInteger& outputChannelsWanted,
@@ -117,10 +122,9 @@ public:
AudioSessionAddPropertyListener (kAudioSessionProperty_AudioRouteChange, routingChangedStatic, this); AudioSessionAddPropertyListener (kAudioSessionProperty_AudioRouteChange, routingChangedStatic, this);
fixAudioRouteIfSetToReceiver(); fixAudioRouteIfSetToReceiver();
updateDeviceInfo();
setSessionFloat64Property (kAudioSessionProperty_PreferredHardwareSampleRate, targetSampleRate); setSessionFloat64Property (kAudioSessionProperty_PreferredHardwareSampleRate, targetSampleRate);
updateSampleRates(); updateDeviceInfo();
setSessionFloat32Property (kAudioSessionProperty_PreferredHardwareIOBufferDuration, preferredBufferSize / sampleRate); setSessionFloat32Property (kAudioSessionProperty_PreferredHardwareIOBufferDuration, preferredBufferSize / sampleRate);
updateCurrentBufferSize(); updateCurrentBufferSize();
@@ -162,8 +166,15 @@ public:
BigInteger getActiveOutputChannels() const override { return activeOutputChans; } BigInteger getActiveOutputChannels() const override { return activeOutputChans; }
BigInteger getActiveInputChannels() const override { return activeInputChans; } BigInteger getActiveInputChannels() const override { return activeInputChans; }
int getOutputLatencyInSamples() override { return 0; } //xxx int getOutputLatencyInSamples() override { return getLatency (kAudioSessionProperty_CurrentHardwareOutputLatency); }
int getInputLatencyInSamples() override { return 0; } //xxx int getInputLatencyInSamples() override { return getLatency (kAudioSessionProperty_CurrentHardwareInputLatency); }
int getLatency (AudioSessionPropertyID propID)
{
Float32 latency = 0;
getSessionProperty (propID, latency);
return roundToInt (latency * getCurrentSampleRate());
}
void start (AudioIODeviceCallback* newCallback) override void start (AudioIODeviceCallback* newCallback) override
{ {
@@ -197,11 +208,16 @@ public:
bool isPlaying() override { return isRunning && callback != nullptr; } bool isPlaying() override { return isRunning && callback != nullptr; }
String getLastError() override { return lastError; } String getLastError() override { return lastError; }
bool setAudioPreprocessingEnabled (bool enable) override
{
return setSessionUInt32Property (kAudioSessionProperty_Mode, enable ? kAudioSessionMode_Default
: kAudioSessionMode_Measurement);
}
private: private:
//================================================================================================== //==================================================================================================
CriticalSection callbackLock; CriticalSection callbackLock;
Float64 sampleRate; Float64 sampleRate;
Array<Float64> sampleRates;
int numInputChannels, numOutputChannels; int numInputChannels, numOutputChannels;
int preferredBufferSize, actualBufferSize; int preferredBufferSize, actualBufferSize;
bool isRunning; bool isRunning;
@@ -322,38 +338,6 @@ private:
getSessionProperty (kAudioSessionProperty_AudioInputAvailable, audioInputIsAvailable); getSessionProperty (kAudioSessionProperty_AudioInputAvailable, audioInputIsAvailable);
} }
void updateSampleRates()
{
getSessionProperty (kAudioSessionProperty_CurrentHardwareSampleRate, sampleRate);
sampleRates.clear();
sampleRates.add (sampleRate);
const int commonSampleRates[] = { 8000, 16000, 22050, 32000, 44100, 48000 };
for (int i = 0; i < numElementsInArray (commonSampleRates); ++i)
{
Float64 rate = (Float64) commonSampleRates[i];
if (rate != sampleRate)
{
setSessionFloat64Property (kAudioSessionProperty_PreferredHardwareSampleRate, rate);
Float64 actualSampleRate = 0.0;
getSessionProperty (kAudioSessionProperty_CurrentHardwareSampleRate, actualSampleRate);
if (actualSampleRate == rate)
sampleRates.add (actualSampleRate);
}
}
DefaultElementComparator<Float64> comparator;
sampleRates.sort (comparator);
setSessionFloat64Property (kAudioSessionProperty_PreferredHardwareSampleRate, sampleRate);
getSessionProperty (kAudioSessionProperty_CurrentHardwareSampleRate, sampleRate);
}
void updateCurrentBufferSize() void updateCurrentBufferSize()
{ {
Float32 bufferDuration = sampleRate > 0 ? (Float32) (preferredBufferSize / sampleRate) : 0.0f; Float32 bufferDuration = sampleRate > 0 ? (Float32) (preferredBufferSize / sampleRate) : 0.0f;
@@ -455,12 +439,12 @@ private:
static OSStatus processStatic (void* client, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time, static OSStatus processStatic (void* client, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time,
UInt32 /*busNumber*/, UInt32 numFrames, AudioBufferList* data) UInt32 /*busNumber*/, UInt32 numFrames, AudioBufferList* data)
{ {
return static_cast <iOSAudioIODevice*> (client)->process (flags, time, numFrames, data); return static_cast<iOSAudioIODevice*> (client)->process (flags, time, numFrames, data);
} }
static void routingChangedStatic (void* client, AudioSessionPropertyID, UInt32 /*inDataSize*/, const void* propertyValue) static void routingChangedStatic (void* client, AudioSessionPropertyID, UInt32 /*inDataSize*/, const void* propertyValue)
{ {
static_cast <iOSAudioIODevice*> (client)->routingChanged (propertyValue); static_cast<iOSAudioIODevice*> (client)->routingChanged (propertyValue);
} }
//================================================================================================== //==================================================================================================
@@ -552,9 +536,9 @@ private:
return AudioSessionGetProperty (propID, &valueSize, &result); return AudioSessionGetProperty (propID, &valueSize, &result);
} }
static void setSessionUInt32Property (AudioSessionPropertyID propID, UInt32 v) noexcept { AudioSessionSetProperty (propID, sizeof (v), &v); } static bool setSessionUInt32Property (AudioSessionPropertyID propID, UInt32 v) noexcept { return AudioSessionSetProperty (propID, sizeof (v), &v) == kAudioSessionNoError; }
static void setSessionFloat32Property (AudioSessionPropertyID propID, Float32 v) noexcept { AudioSessionSetProperty (propID, sizeof (v), &v); } static bool setSessionFloat32Property (AudioSessionPropertyID propID, Float32 v) noexcept { return AudioSessionSetProperty (propID, sizeof (v), &v) == kAudioSessionNoError; }
static void setSessionFloat64Property (AudioSessionPropertyID propID, Float64 v) noexcept { AudioSessionSetProperty (propID, sizeof (v), &v); } static bool setSessionFloat64Property (AudioSessionPropertyID propID, Float64 v) noexcept { return AudioSessionSetProperty (propID, sizeof (v), &v) == kAudioSessionNoError; }
JUCE_DECLARE_NON_COPYABLE (iOSAudioIODevice) JUCE_DECLARE_NON_COPYABLE (iOSAudioIODevice)
}; };
@@ -41,7 +41,7 @@ namespace
return err; return err;
} }
#else #else
#define JUCE_ALSA_LOG(x) #define JUCE_ALSA_LOG(x) {}
#define JUCE_CHECKED_RESULT(x) (x) #define JUCE_CHECKED_RESULT(x) (x)
#endif #endif
@@ -144,6 +144,7 @@ public:
: owner (d), : owner (d),
inputLatency (0), inputLatency (0),
outputLatency (0), outputLatency (0),
bitDepth (32),
callback (nullptr), callback (nullptr),
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
audioProcID (0), audioProcID (0),
@@ -232,7 +233,7 @@ public:
size = sizeof (nameNSString); size = sizeof (nameNSString);
pa.mSelector = kAudioObjectPropertyElementName; pa.mSelector = kAudioObjectPropertyElementName;
pa.mElement = chanNum + 1; pa.mElement = (AudioObjectPropertyElement) chanNum + 1;
if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &nameNSString) == noErr) if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &nameNSString) == noErr)
{ {
@@ -352,6 +353,22 @@ public:
return (int) lat; return (int) lat;
} }
int getBitDepthFromDevice (AudioObjectPropertyScope scope) const
{
AudioObjectPropertyAddress pa;
pa.mElement = kAudioObjectPropertyElementMaster;
pa.mSelector = kAudioStreamPropertyPhysicalFormat;
pa.mScope = scope;
AudioStreamBasicDescription asbd;
UInt32 size = sizeof (asbd);
if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &asbd)))
return (int) asbd.mBitsPerChannel;
return 0;
}
void updateDetailsFromDevice() void updateDetailsFromDevice()
{ {
stopTimer(); stopTimer();
@@ -377,7 +394,7 @@ public:
if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &sr))) if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &sr)))
sampleRate = sr; sampleRate = sr;
UInt32 framesPerBuf = bufferSize; UInt32 framesPerBuf = (UInt32) bufferSize;
size = sizeof (framesPerBuf); size = sizeof (framesPerBuf);
pa.mSelector = kAudioDevicePropertyBufferFrameSize; pa.mSelector = kAudioDevicePropertyBufferFrameSize;
AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &framesPerBuf); AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &framesPerBuf);
@@ -392,6 +409,13 @@ public:
StringArray newInNames (getChannelInfo (true, newInChans)); StringArray newInNames (getChannelInfo (true, newInChans));
StringArray newOutNames (getChannelInfo (false, newOutChans)); StringArray newOutNames (getChannelInfo (false, newOutChans));
const int inputBitDepth = getBitDepthFromDevice (kAudioDevicePropertyScopeInput);
const int outputBitDepth = getBitDepthFromDevice (kAudioDevicePropertyScopeOutput);
bitDepth = jmax (inputBitDepth, outputBitDepth);
if (bitDepth <= 0)
bitDepth = 32;
// after getting the new values, lock + apply them // after getting the new values, lock + apply them
const ScopedLock sl (callbackLock); const ScopedLock sl (callbackLock);
@@ -728,6 +752,7 @@ public:
//============================================================================== //==============================================================================
CoreAudioIODevice& owner; CoreAudioIODevice& owner;
int inputLatency, outputLatency; int inputLatency, outputLatency;
int bitDepth;
BigInteger activeInputChans, activeOutputChans; BigInteger activeInputChans, activeOutputChans;
StringArray inChanNames, outChanNames; StringArray inChanNames, outChanNames;
Array<double> sampleRates; Array<double> sampleRates;
@@ -886,7 +911,7 @@ public:
Array<int> getAvailableBufferSizes() override { return internal->bufferSizes; } Array<int> getAvailableBufferSizes() override { return internal->bufferSizes; }
double getCurrentSampleRate() override { return internal->getSampleRate(); } double getCurrentSampleRate() override { return internal->getSampleRate(); }
int getCurrentBitDepth() override { return 32; } // no way to find out, so just assume it's high.. int getCurrentBitDepth() override { return internal->bitDepth; }
int getCurrentBufferSizeSamples() override { return internal->getBufferSize(); } int getCurrentBufferSizeSamples() override { return internal->getBufferSize(); }
int getDefaultBufferSize() override int getDefaultBufferSize() override
@@ -1387,7 +1412,7 @@ private:
d.done = (d.numInputChans == 0); d.done = (d.numInputChans == 0);
} }
for (int tries = 3;;) for (int tries = 5;;)
{ {
bool anyRemaining = false; bool anyRemaining = false;
@@ -1434,7 +1459,7 @@ private:
d.done = (d.numOutputChans == 0); d.done = (d.numOutputChans == 0);
} }
for (int tries = 3;;) for (int tries = 5;;)
{ {
bool anyRemaining = false; bool anyRemaining = false;
@@ -365,7 +365,7 @@ public:
void updateSampleRates() void updateSampleRates()
{ {
// find a list of sample rates.. // find a list of sample rates..
const int possibleSampleRates[] = { 44100, 48000, 88200, 96000, 176400, 192000 }; const int possibleSampleRates[] = { 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000 };
Array<double> newRates; Array<double> newRates;
if (asioObject != nullptr) if (asioObject != nullptr)
@@ -33,8 +33,8 @@ namespace WasapiClasses
void logFailure (HRESULT hr) void logFailure (HRESULT hr)
{ {
(void) hr; (void) hr;
jassert (hr != 0x800401f0); // If you hit this, it means you're trying to call from jassert (hr != (HRESULT) 0x800401f0); // If you hit this, it means you're trying to call from
// a thread which hasn't been initialised with CoInitialize(). // a thread which hasn't been initialised with CoInitialize().
#if JUCE_WASAPI_LOGGING #if JUCE_WASAPI_LOGGING
if (FAILED (hr)) if (FAILED (hr))
@@ -182,6 +182,10 @@ void AudioTransportSource::setNextReadPosition (int64 newPosition)
newPosition = (int64) (newPosition * sourceSampleRate / sampleRate); newPosition = (int64) (newPosition * sourceSampleRate / sampleRate);
positionableSource->setNextReadPosition (newPosition); positionableSource->setNextReadPosition (newPosition);
if (resamplerSource != nullptr)
resamplerSource->flushBuffers();
inputStreamEOF = false; inputStreamEOF = false;
} }
} }
@@ -37,7 +37,7 @@ const char* const AiffAudioFormat::appleKey = "apple key";
//============================================================================== //==============================================================================
namespace AiffFileHelpers namespace AiffFileHelpers
{ {
inline int chunkName (const char* const name) { return (int) ByteOrder::littleEndianInt (name); } inline int chunkName (const char* name) noexcept { return (int) ByteOrder::littleEndianInt (name); }
#if JUCE_MSVC #if JUCE_MSVC
#pragma pack (push, 1) #pragma pack (push, 1)
@@ -888,13 +888,13 @@ AiffAudioFormat::~AiffAudioFormat()
Array<int> AiffAudioFormat::getPossibleSampleRates() Array<int> AiffAudioFormat::getPossibleSampleRates()
{ {
const int rates[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 }; const int rates[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 };
return Array <int> (rates); return Array<int> (rates);
} }
Array<int> AiffAudioFormat::getPossibleBitDepths() Array<int> AiffAudioFormat::getPossibleBitDepths()
{ {
const int depths[] = { 8, 16, 24, 0 }; const int depths[] = { 8, 16, 24, 0 };
return Array <int> (depths); return Array<int> (depths);
} }
bool AiffAudioFormat::canDoStereo() { return true; } bool AiffAudioFormat::canDoStereo() { return true; }
@@ -165,13 +165,13 @@ bool LAMEEncoderAudioFormat::canHandleFile (const File&)
Array<int> LAMEEncoderAudioFormat::getPossibleSampleRates() Array<int> LAMEEncoderAudioFormat::getPossibleSampleRates()
{ {
const int rates[] = { 32000, 44100, 48000, 0 }; const int rates[] = { 32000, 44100, 48000, 0 };
return Array <int> (rates); return Array<int> (rates);
} }
Array<int> LAMEEncoderAudioFormat::getPossibleBitDepths() Array<int> LAMEEncoderAudioFormat::getPossibleBitDepths()
{ {
const int depths[] = { 16, 0 }; const int depths[] = { 16, 0 };
return Array <int> (depths); return Array<int> (depths);
} }
bool LAMEEncoderAudioFormat::canDoStereo() { return true; } bool LAMEEncoderAudioFormat::canDoStereo() { return true; }
@@ -65,6 +65,7 @@ const char* const WavAudioFormat::acidNumerator = "acid numerator";
const char* const WavAudioFormat::acidTempo = "acid tempo"; const char* const WavAudioFormat::acidTempo = "acid tempo";
const char* const WavAudioFormat::ISRC = "ISRC"; const char* const WavAudioFormat::ISRC = "ISRC";
const char* const WavAudioFormat::tracktionLoopInfo = "tracktion loop info";
//============================================================================== //==============================================================================
namespace WavFileHelpers namespace WavFileHelpers
@@ -471,6 +472,46 @@ namespace WavFileHelpers
} }
} }
//==============================================================================
namespace ListInfoChunk
{
static bool writeValue (const StringPairArray& values, MemoryOutputStream& out, const char* paramName)
{
const String value (values.getValue (paramName, String()));
if (value.isEmpty())
return false;
const int valueLength = (int) value.getNumBytesAsUTF8() + 1;
const int chunkLength = valueLength + (valueLength & 1);
out.writeInt (chunkName (paramName));
out.writeInt (chunkLength);
out.write (value.toUTF8(), (size_t) valueLength);
if ((out.getDataSize() & 1) != 0)
out.writeByte (0);
return true;
}
static MemoryBlock createFrom (const StringPairArray& values)
{
static const char* params[] = { "INAM", "IART", "IPRD", "IPRT", "ISFT",
"ISRC", "IGNR", "ICMT", "ICOP", "ICRD" };
MemoryOutputStream out;
out.writeInt (chunkName ("INFO"));
bool anyParamsDefined = false;
for (int i = 0; i < numElementsInArray (params); ++i)
if (writeValue (values, out, params[i]))
anyParamsDefined = true;
return anyParamsDefined ? out.getMemoryBlock() : MemoryBlock();
}
}
//============================================================================== //==============================================================================
struct AcidChunk struct AcidChunk
{ {
@@ -481,6 +522,38 @@ namespace WavFileHelpers
input.read (this, (int) jmin (sizeof (*this), length)); input.read (this, (int) jmin (sizeof (*this), length));
} }
AcidChunk (const StringPairArray& values)
{
zerostruct (*this);
flags = getFlagIfPresent (values, WavAudioFormat::acidOneShot, 0x01)
| getFlagIfPresent (values, WavAudioFormat::acidRootSet, 0x02)
| getFlagIfPresent (values, WavAudioFormat::acidStretch, 0x04)
| getFlagIfPresent (values, WavAudioFormat::acidDiskBased, 0x08)
| getFlagIfPresent (values, WavAudioFormat::acidizerFlag, 0x10);
if (values[WavAudioFormat::acidRootSet].getIntValue() != 0)
rootNote = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidRootNote].getIntValue());
numBeats = ByteOrder::swapIfBigEndian ((uint32) values[WavAudioFormat::acidBeats].getIntValue());
meterDenominator = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidDenominator].getIntValue());
meterNumerator = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidNumerator].getIntValue());
if (values.containsKey (WavAudioFormat::acidTempo))
tempo = swapFloatByteOrder (values[WavAudioFormat::acidTempo].getFloatValue());
}
static MemoryBlock createFrom (const StringPairArray& values)
{
return AcidChunk (values).toMemoryBlock();
}
MemoryBlock toMemoryBlock() const
{
return (flags != 0 || rootNote != 0 || numBeats != 0 || meterDenominator != 0 || meterNumerator != 0)
? MemoryBlock (this, sizeof (*this)) : MemoryBlock();
}
void addToMetadata (StringPairArray& values) const void addToMetadata (StringPairArray& values) const
{ {
setBoolFlag (values, WavAudioFormat::acidOneShot, 0x01); setBoolFlag (values, WavAudioFormat::acidOneShot, 0x01);
@@ -490,30 +563,65 @@ namespace WavFileHelpers
setBoolFlag (values, WavAudioFormat::acidizerFlag, 0x10); setBoolFlag (values, WavAudioFormat::acidizerFlag, 0x10);
if (flags & 0x02) // root note set if (flags & 0x02) // root note set
values.set (WavAudioFormat::acidRootNote, String (rootNote)); values.set (WavAudioFormat::acidRootNote, String (ByteOrder::swapIfBigEndian (rootNote)));
values.set (WavAudioFormat::acidBeats, String (numBeats)); values.set (WavAudioFormat::acidBeats, String (ByteOrder::swapIfBigEndian (numBeats)));
values.set (WavAudioFormat::acidDenominator, String (meterDenominator)); values.set (WavAudioFormat::acidDenominator, String (ByteOrder::swapIfBigEndian (meterDenominator)));
values.set (WavAudioFormat::acidNumerator, String (meterNumerator)); values.set (WavAudioFormat::acidNumerator, String (ByteOrder::swapIfBigEndian (meterNumerator)));
values.set (WavAudioFormat::acidTempo, String (tempo)); values.set (WavAudioFormat::acidTempo, String (swapFloatByteOrder (tempo)));
} }
void setBoolFlag (StringPairArray& values, const char* name, int32 mask) const void setBoolFlag (StringPairArray& values, const char* name, uint32 mask) const
{ {
values.set (name, (flags & mask) ? "1" : "0"); values.set (name, (flags & ByteOrder::swapIfBigEndian (mask)) ? "1" : "0");
} }
int32 flags; static uint32 getFlagIfPresent (const StringPairArray& values, const char* name, uint32 flag)
int16 rootNote; {
int16 reserved1; return values[name].getIntValue() != 0 ? ByteOrder::swapIfBigEndian (flag) : 0;
}
static float swapFloatByteOrder (const float x) noexcept
{
#ifdef JUCE_BIG_ENDIAN
union { uint32 asInt; float asFloat; } n;
n.asFloat = x;
n.asInt = ByteOrder::swap (n.asInt);
return n.asFloat;
#else
return x;
#endif
}
uint32 flags;
uint16 rootNote;
uint16 reserved1;
float reserved2; float reserved2;
int32 numBeats; uint32 numBeats;
int16 meterDenominator; uint16 meterDenominator;
int16 meterNumerator; uint16 meterNumerator;
float tempo; float tempo;
} JUCE_PACKED; } JUCE_PACKED;
//==============================================================================
struct TracktionChunk
{
static MemoryBlock createFrom (const StringPairArray& values)
{
const String s = values[WavAudioFormat::tracktionLoopInfo];
MemoryBlock data;
if (s.isNotEmpty())
{
MemoryOutputStream os (data, false);
os.writeString (s);
}
return data;
}
};
//============================================================================== //==============================================================================
namespace AXMLChunk namespace AXMLChunk
{ {
@@ -816,6 +924,12 @@ public:
{ {
AcidChunk (*input, length).addToMetadata (metadataValues); AcidChunk (*input, length).addToMetadata (metadataValues);
} }
else if (chunkType == chunkName ("Trkn"))
{
MemoryBlock tracktion;
input->readIntoMemoryBlock (tracktion, (ssize_t) length);
metadataValues.set (WavAudioFormat::tracktionLoopInfo, tracktion.toString());
}
else if (chunkEnd <= input->getPosition()) else if (chunkEnd <= input->getPosition())
{ {
break; break;
@@ -913,12 +1027,15 @@ public:
// key should be removed (or set to "WAV") once this has been done // key should be removed (or set to "WAV") once this has been done
jassert (metadataValues.getValue ("MetaDataSource", "None") != "AIFF"); jassert (metadataValues.getValue ("MetaDataSource", "None") != "AIFF");
bwavChunk = BWAVChunk::createFrom (metadataValues); bwavChunk = BWAVChunk::createFrom (metadataValues);
axmlChunk = AXMLChunk::createFrom (metadataValues); axmlChunk = AXMLChunk::createFrom (metadataValues);
smplChunk = SMPLChunk::createFrom (metadataValues); smplChunk = SMPLChunk::createFrom (metadataValues);
instChunk = InstChunk::createFrom (metadataValues); instChunk = InstChunk::createFrom (metadataValues);
cueChunk = CueChunk ::createFrom (metadataValues); cueChunk = CueChunk ::createFrom (metadataValues);
listChunk = ListChunk::createFrom (metadataValues); listChunk = ListChunk::createFrom (metadataValues);
listInfoChunk = ListInfoChunk::createFrom (metadataValues);
acidChunk = AcidChunk::createFrom (metadataValues);
trckChunk = TracktionChunk::createFrom (metadataValues);
} }
headerPosition = out->getPosition(); headerPosition = out->getPosition();
@@ -927,12 +1044,6 @@ public:
~WavAudioFormatWriter() ~WavAudioFormatWriter()
{ {
if ((bytesWritten & 1) != 0) // pad to an even length
{
++bytesWritten;
output->writeByte (0);
}
writeHeader(); writeHeader();
} }
@@ -972,8 +1083,22 @@ public:
return true; return true;
} }
bool flush() override
{
const int64 lastWritePos = output->getPosition();
writeHeader();
if (output->setPosition (lastWritePos))
return true;
// if this fails, you've given it an output stream that can't seek! It needs
// to be able to seek back to write the header
jassertfalse;
return false;
}
private: private:
MemoryBlock tempBlock, bwavChunk, axmlChunk, smplChunk, instChunk, cueChunk, listChunk; MemoryBlock tempBlock, bwavChunk, axmlChunk, smplChunk, instChunk, cueChunk, listChunk, listInfoChunk, acidChunk, trckChunk;
uint64 lengthInSamples, bytesWritten; uint64 lengthInSamples, bytesWritten;
int64 headerPosition; int64 headerPosition;
bool writeFailed; bool writeFailed;
@@ -998,13 +1123,18 @@ private:
void writeHeader() void writeHeader()
{ {
using namespace WavFileHelpers; if ((bytesWritten & 1) != 0) // pad to an even length
const bool seekedOk = output->setPosition (headerPosition); output->writeByte (0);
(void) seekedOk;
// if this fails, you've given it an output stream that can't seek! It needs using namespace WavFileHelpers;
// to be able to seek back to write the header
jassert (seekedOk); if (headerPosition != output->getPosition() && ! output->setPosition (headerPosition))
{
// if this fails, you've given it an output stream that can't seek! It needs to be
// able to seek back to go back and write the header after the data has been written.
jassertfalse;
return;
}
const size_t bytesPerFrame = numChannels * bitsPerSample / 8; const size_t bytesPerFrame = numChannels * bitsPerSample / 8;
uint64 audioDataSize = bytesPerFrame * lengthInSamples; uint64 audioDataSize = bytesPerFrame * lengthInSamples;
@@ -1020,6 +1150,9 @@ private:
+ chunkSize (instChunk) + chunkSize (instChunk)
+ chunkSize (cueChunk) + chunkSize (cueChunk)
+ chunkSize (listChunk) + chunkSize (listChunk)
+ chunkSize (listInfoChunk)
+ chunkSize (acidChunk)
+ chunkSize (trckChunk)
+ (8 + 28)); // (ds64 chunk) + (8 + 28)); // (ds64 chunk)
riffChunkSize += (riffChunkSize & 1); riffChunkSize += (riffChunkSize & 1);
@@ -1093,12 +1226,15 @@ private:
output->write (subFormat.data4, sizeof (subFormat.data4)); output->write (subFormat.data4, sizeof (subFormat.data4));
} }
writeChunk (bwavChunk, chunkName ("bext")); writeChunk (bwavChunk, chunkName ("bext"));
writeChunk (axmlChunk, chunkName ("axml")); writeChunk (axmlChunk, chunkName ("axml"));
writeChunk (smplChunk, chunkName ("smpl")); writeChunk (smplChunk, chunkName ("smpl"));
writeChunk (instChunk, chunkName ("inst"), 7); writeChunk (instChunk, chunkName ("inst"), 7);
writeChunk (cueChunk, chunkName ("cue ")); writeChunk (cueChunk, chunkName ("cue "));
writeChunk (listChunk, chunkName ("LIST")); writeChunk (listChunk, chunkName ("LIST"));
writeChunk (listInfoChunk, chunkName ("LIST"));
writeChunk (acidChunk, chunkName ("acid"));
writeChunk (trckChunk, chunkName ("Trkn"));
writeChunkHeader (chunkName ("data"), isRF64 ? -1 : (int) (lengthInSamples * bytesPerFrame)); writeChunkHeader (chunkName ("data"), isRF64 ? -1 : (int) (lengthInSamples * bytesPerFrame));
@@ -135,6 +135,9 @@ public:
/** Metadata property name used when reading an ISRC code from an AXML chunk. */ /** Metadata property name used when reading an ISRC code from an AXML chunk. */
static const char* const ISRC; static const char* const ISRC;
/** Metadata property name used when reading a WAV file with a Tracktion chunk. */
static const char* const tracktionLoopInfo;
//============================================================================== //==============================================================================
Array<int> getPossibleSampleRates() override; Array<int> getPossibleSampleRates() override;
Array<int> getPossibleBitDepths() override; Array<int> getPossibleBitDepths() override;
@@ -162,7 +162,7 @@ void AudioFormatReader::read (AudioSampleBuffer* buffer,
} }
else else
{ {
HeapBlock<int*> chans (numTargetChannels); HeapBlock<int*> chans ((size_t) numTargetChannels);
readChannels (*this, chans, buffer, startSample, numSamples, readerStartSample, numTargetChannels); readChannels (*this, chans, buffer, startSample, numSamples, readerStartSample, numTargetChannels);
} }
@@ -173,35 +173,54 @@ void AudioFormatReader::read (AudioSampleBuffer* buffer,
} }
} }
template <typename SampleType> void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples,
static Range<SampleType> getChannelMinAndMax (SampleType* channel, int numSamples) noexcept Range<float>* const results, const int channelsToRead)
{ {
return Range<SampleType>::findMinAndMax (channel, numSamples); jassert (channelsToRead > 0 && channelsToRead <= (int) numChannels);
}
static Range<float> getChannelMinAndMax (float* channel, int numSamples) noexcept if (numSamples <= 0)
{
return FloatVectorOperations::findMinAndMax (channel, numSamples);
}
template <typename SampleType>
static void getStereoMinAndMax (SampleType* const* channels, const int numChannels, const int numSamples,
SampleType& lmin, SampleType& lmax, SampleType& rmin, SampleType& rmax)
{
Range<SampleType> range (getChannelMinAndMax (channels[0], numSamples));
lmax = jmax (lmax, range.getEnd());
lmin = jmin (lmin, range.getStart());
if (numChannels > 1)
{ {
range = getChannelMinAndMax (channels[1], numSamples); for (int i = 0; i < channelsToRead; ++i)
rmax = jmax (rmax, range.getEnd()); results[i] = Range<float>();
rmin = jmin (rmin, range.getStart());
return;
} }
else
const int bufferSize = (int) jmin (numSamples, (int64) 4096);
AudioSampleBuffer tempSampleBuffer ((int) channelsToRead, bufferSize);
float* const* const floatBuffer = tempSampleBuffer.getArrayOfWritePointers();
int* const* intBuffer = reinterpret_cast<int* const*> (floatBuffer);
bool isFirstBlock = true;
while (numSamples > 0)
{ {
rmax = lmax; const int numToDo = (int) jmin (numSamples, (int64) bufferSize);
rmin = lmin; if (! read (intBuffer, channelsToRead, startSampleInFile, numToDo, false))
break;
for (int i = 0; i < channelsToRead; ++i)
{
Range<float> r;
if (usesFloatingPointData)
{
r = FloatVectorOperations::findMinAndMax (floatBuffer[i], numToDo);
}
else
{
Range<int> intRange (Range<int>::findMinAndMax (intBuffer[i], numToDo));
r = Range<float> (intRange.getStart() / (float) std::numeric_limits<int>::max(),
intRange.getEnd() / (float) std::numeric_limits<int>::max());
}
results[i] = isFirstBlock ? r : results[i].getUnionWith (r);
}
isFirstBlock = false;
numSamples -= numToDo;
startSampleInFile += numToDo;
} }
} }
@@ -209,66 +228,20 @@ void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples
float& lowestLeft, float& highestLeft, float& lowestLeft, float& highestLeft,
float& lowestRight, float& highestRight) float& lowestRight, float& highestRight)
{ {
if (numSamples <= 0) Range<float> levels[2];
readMaxLevels (startSampleInFile, numSamples, levels, jmin (2, (int) numChannels));
lowestLeft = levels[0].getStart();
highestLeft = levels[0].getEnd();
if (numChannels > 1)
{ {
lowestLeft = 0; lowestRight = levels[1].getStart();
lowestRight = 0; highestRight = levels[1].getEnd();
highestLeft = 0;
highestRight = 0;
return;
}
const int bufferSize = (int) jmin (numSamples, (int64) 4096);
AudioSampleBuffer tempSampleBuffer ((int) numChannels, bufferSize);
float* const* const floatBuffer = tempSampleBuffer.getArrayOfWritePointers();
int* const* intBuffer = reinterpret_cast<int* const*> (floatBuffer);
if (usesFloatingPointData)
{
float lmin = 1.0e6f;
float lmax = -lmin;
float rmin = lmin;
float rmax = lmax;
while (numSamples > 0)
{
const int numToDo = (int) jmin (numSamples, (int64) bufferSize);
if (! read (intBuffer, 2, startSampleInFile, numToDo, false))
break;
numSamples -= numToDo;
startSampleInFile += numToDo;
getStereoMinAndMax (floatBuffer, (int) numChannels, numToDo, lmin, lmax, rmin, rmax);
}
lowestLeft = lmin;
highestLeft = lmax;
lowestRight = rmin;
highestRight = rmax;
} }
else else
{ {
int lmax = std::numeric_limits<int>::min(); lowestRight = lowestLeft;
int lmin = std::numeric_limits<int>::max(); highestRight = highestLeft;
int rmax = std::numeric_limits<int>::min();
int rmin = std::numeric_limits<int>::max();
while (numSamples > 0)
{
const int numToDo = (int) jmin (numSamples, (int64) bufferSize);
if (! read (intBuffer, 2, startSampleInFile, numToDo, false))
break;
numSamples -= numToDo;
startSampleInFile += numToDo;
getStereoMinAndMax (intBuffer, (int) numChannels, numToDo, lmin, lmax, rmin, rmax);
}
lowestLeft = lmin / (float) std::numeric_limits<int>::max();
highestLeft = lmax / (float) std::numeric_limits<int>::max();
lowestRight = rmin / (float) std::numeric_limits<int>::max();
highestRight = rmax / (float) std::numeric_limits<int>::max();
} }
} }
@@ -121,6 +121,25 @@ public:
bool useReaderLeftChan, bool useReaderLeftChan,
bool useReaderRightChan); bool useReaderRightChan);
/** Finds the highest and lowest sample levels from a section of the audio stream.
This will read a block of samples from the stream, and measure the
highest and lowest sample levels from the channels in that section, returning
these as normalised floating-point levels.
@param startSample the offset into the audio stream to start reading from. It's
ok for this to be beyond the start or end of the stream.
@param numSamples how many samples to read
@param results this array will be filled with Range values for each channel.
The array must contain numChannels elements.
@param numChannelsToRead the number of channels of data to scan. This must be
more than zero, but not more than the total number of channels
that the reader contains
@see read
*/
virtual void readMaxLevels (int64 startSample, int64 numSamples,
Range<float>* results, int numChannelsToRead);
/** Finds the highest and lowest sample levels from a section of the audio stream. /** Finds the highest and lowest sample levels from a section of the audio stream.
This will read a block of samples from the stream, and measure the This will read a block of samples from the stream, and measure the
@@ -138,12 +157,9 @@ public:
channel (if there is one) channel (if there is one)
@see read @see read
*/ */
virtual void readMaxLevels (int64 startSample, virtual void readMaxLevels (int64 startSample, int64 numSamples,
int64 numSamples, float& lowestLeft, float& highestLeft,
float& lowestLeft, float& lowestRight, float& highestRight);
float& highestLeft,
float& lowestRight,
float& highestRight);
/** Scans the source looking for a sample whose magnitude is in a specified range. /** Scans the source looking for a sample whose magnitude is in a specified range.
@@ -183,6 +183,11 @@ bool AudioFormatWriter::writeFromAudioSampleBuffer (const AudioSampleBuffer& sou
return writeFromFloatArrays (chans, numSourceChannels, numSamples); return writeFromFloatArrays (chans, numSourceChannels, numSamples);
} }
bool AudioFormatWriter::flush()
{
return false;
}
//============================================================================== //==============================================================================
class AudioFormatWriter::ThreadedWriter::Buffer : private TimeSliceClient class AudioFormatWriter::ThreadedWriter::Buffer : private TimeSliceClient
{ {
@@ -194,6 +199,8 @@ public:
writer (w), writer (w),
receiver (nullptr), receiver (nullptr),
samplesWritten (0), samplesWritten (0),
samplesPerFlush (0),
flushSampleCounter (0),
isRunning (true) isRunning (true)
{ {
timeSliceThread.addTimeSliceClient (this); timeSliceThread.addTimeSliceClient (this);
@@ -266,6 +273,18 @@ public:
} }
fifo.finishedRead (size1 + size2); fifo.finishedRead (size1 + size2);
if (samplesPerFlush > 0)
{
flushSampleCounter -= size1 + size2;
if (flushSampleCounter <= 0)
{
flushSampleCounter = samplesPerFlush;
writer->flush();
}
}
return 0; return 0;
} }
@@ -279,6 +298,11 @@ public:
samplesWritten = 0; samplesWritten = 0;
} }
void setFlushInterval (int numSamples) noexcept
{
samplesPerFlush = numSamples;
}
private: private:
AbstractFifo fifo; AbstractFifo fifo;
AudioSampleBuffer buffer; AudioSampleBuffer buffer;
@@ -287,6 +311,7 @@ private:
CriticalSection thumbnailLock; CriticalSection thumbnailLock;
IncomingDataReceiver* receiver; IncomingDataReceiver* receiver;
int64 samplesWritten; int64 samplesWritten;
int samplesPerFlush, flushSampleCounter;
volatile bool isRunning; volatile bool isRunning;
JUCE_DECLARE_NON_COPYABLE (Buffer) JUCE_DECLARE_NON_COPYABLE (Buffer)
@@ -310,3 +335,8 @@ void AudioFormatWriter::ThreadedWriter::setDataReceiver (AudioFormatWriter::Thre
{ {
buffer->setDataReceiver (receiver); buffer->setDataReceiver (receiver);
} }
void AudioFormatWriter::ThreadedWriter::setFlushInterval (int numSamplesPerFlush) noexcept
{
buffer->setFlushInterval (numSamplesPerFlush);
}
@@ -91,8 +91,18 @@ public:
to pass it into the method. to pass it into the method.
@param numSamples the number of samples to write @param numSamples the number of samples to write
*/ */
virtual bool write (const int** samplesToWrite, virtual bool write (const int** samplesToWrite, int numSamples) = 0;
int numSamples) = 0;
/** Some formats may support a flush operation that makes sure the file is in a
valid state before carrying on.
If supported, this means that by calling flush periodically when writing data
to a large file, then it should still be left in a readable state if your program
crashes.
It goes without saying that this method must be called from the same thread that's
calling write()!
If the format supports flushing and the operation succeeds, this returns true.
*/
virtual bool flush();
//============================================================================== //==============================================================================
/** Reads a section of samples from an AudioFormatReader, and writes these to /** Reads a section of samples from an AudioFormatReader, and writes these to
@@ -197,7 +207,12 @@ public:
The object passed-in must not be deleted while this writer is still using it. The object passed-in must not be deleted while this writer is still using it.
*/ */
void setDataReceiver (IncomingDataReceiver* receiver); void setDataReceiver (IncomingDataReceiver*);
/** Sets how many samples should be written before calling the AudioFormatWriter::flush method.
Set this to 0 to disable flushing (this is the default).
*/
void setFlushInterval (int numSamplesPerFlush) noexcept;
private: private:
class Buffer; class Buffer;
@@ -1,7 +1,7 @@
{ {
"id": "juce_audio_formats", "id": "juce_audio_formats",
"name": "JUCE audio file format codecs", "name": "JUCE audio file format codecs",
"version": "3.0.5", "version": "3.0.8",
"description": "Classes for reading and writing various audio file formats.", "description": "Classes for reading and writing various audio file formats.",
"website": "http://www.juce.com/juce", "website": "http://www.juce.com/juce",
"license": "GPL/Commercial", "license": "GPL/Commercial",
@@ -59,12 +59,12 @@ SamplerSound::~SamplerSound()
{ {
} }
bool SamplerSound::appliesToNote (const int midiNoteNumber) bool SamplerSound::appliesToNote (int midiNoteNumber)
{ {
return midiNotes [midiNoteNumber]; return midiNotes [midiNoteNumber];
} }
bool SamplerSound::appliesToChannel (const int /*midiChannel*/) bool SamplerSound::appliesToChannel (int /*midiChannel*/)
{ {
return true; return true;
} }
@@ -127,7 +127,7 @@ void SamplerVoice::startNote (const int midiNoteNumber,
} }
} }
void SamplerVoice::stopNote (const bool allowTailOff) void SamplerVoice::stopNote (float /*velocity*/, bool allowTailOff)
{ {
if (allowTailOff) if (allowTailOff)
{ {
@@ -197,7 +197,7 @@ void SamplerVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSa
if (attackReleaseLevel <= 0.0f) if (attackReleaseLevel <= 0.0f)
{ {
stopNote (false); stopNote (0.0f, false);
break; break;
} }
} }
@@ -216,7 +216,7 @@ void SamplerVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSa
if (sourceSamplePosition > playingSound->length) if (sourceSamplePosition > playingSound->length)
{ {
stopNote (false); stopNote (0.0f, false);
break; break;
} }
} }
@@ -82,8 +82,8 @@ public:
//============================================================================== //==============================================================================
bool appliesToNote (const int midiNoteNumber) override; bool appliesToNote (int midiNoteNumber) override;
bool appliesToChannel (const int midiChannel) override; bool appliesToChannel (int midiChannel) override;
private: private:
@@ -124,7 +124,7 @@ public:
bool canPlaySound (SynthesiserSound*) override; bool canPlaySound (SynthesiserSound*) override;
void startNote (int midiNoteNumber, float velocity, SynthesiserSound*, int pitchWheel) override; void startNote (int midiNoteNumber, float velocity, SynthesiserSound*, int pitchWheel) override;
void stopNote (bool allowTailOff) override; void stopNote (float velocity, bool allowTailOff) override;
void pitchWheelMoved (int newValue); void pitchWheelMoved (int newValue);
void controllerMoved (int controllerNumber, int newValue) override; void controllerMoved (int controllerNumber, int newValue) override;
@@ -54,7 +54,7 @@ public:
(e.g. VST shells) can use a single DLL to create a set of different plugin (e.g. VST shells) can use a single DLL to create a set of different plugin
subtypes, so in that case, each subtype is returned as a separate object. subtypes, so in that case, each subtype is returned as a separate object.
*/ */
virtual void findAllTypesForFile (OwnedArray <PluginDescription>& results, virtual void findAllTypesForFile (OwnedArray<PluginDescription>& results,
const String& fileOrIdentifier) = 0; const String& fileOrIdentifier) = 0;
/** Tries to recreate a type from a previously generated PluginDescription. /** Tries to recreate a type from a previously generated PluginDescription.
@@ -89,7 +89,7 @@ public:
private: private:
//============================================================================== //==============================================================================
OwnedArray <AudioPluginFormat> formats; OwnedArray<AudioPluginFormat> formats;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginFormatManager) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginFormatManager)
}; };
@@ -37,7 +37,7 @@ public:
//============================================================================== //==============================================================================
String getName() const override { return "AudioUnit"; } String getName() const override { return "AudioUnit"; }
void findAllTypesForFile (OwnedArray <PluginDescription>&, const String& fileOrIdentifier) override; void findAllTypesForFile (OwnedArray<PluginDescription>&, const String& fileOrIdentifier) override;
AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc, double, int) override; AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc, double, int) override;
bool fileMightContainThisPluginType (const String& fileOrIdentifier) override; bool fileMightContainThisPluginType (const String& fileOrIdentifier) override;
String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override; String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override;
@@ -62,7 +62,7 @@ namespace AudioUnitFormatHelpers
static ThreadLocalValue<int> insideCallback; static ThreadLocalValue<int> insideCallback;
#endif #endif
String osTypeToString (OSType type) String osTypeToString (OSType type) noexcept
{ {
const juce_wchar s[4] = { (juce_wchar) ((type >> 24) & 0xff), const juce_wchar s[4] = { (juce_wchar) ((type >> 24) & 0xff),
(juce_wchar) ((type >> 16) & 0xff), (juce_wchar) ((type >> 16) & 0xff),
@@ -88,9 +88,6 @@ namespace AudioUnitFormatHelpers
String createPluginIdentifier (const AudioComponentDescription& desc) String createPluginIdentifier (const AudioComponentDescription& desc)
{ {
jassert (osTypeToString ('abcd') == "abcd"); // agh, must have got the endianness wrong..
jassert (stringToOSType ("abcd") == (OSType) 'abcd'); // ditto
String s (auIdentifierPrefix); String s (auIdentifierPrefix);
if (desc.componentType == kAudioUnitType_MusicDevice) if (desc.componentType == kAudioUnitType_MusicDevice)
@@ -138,7 +135,7 @@ namespace AudioUnitFormatHelpers
fileOrIdentifier.lastIndexOfChar ('/')) + 1)); fileOrIdentifier.lastIndexOfChar ('/')) + 1));
StringArray tokens; StringArray tokens;
tokens.addTokens (s, ",", String()); tokens.addTokens (s, ",", StringRef());
tokens.removeEmptyStrings(); tokens.removeEmptyStrings();
if (tokens.size() == 3) if (tokens.size() == 3)
@@ -215,9 +212,11 @@ namespace AudioUnitFormatHelpers
const short resFileId = CFBundleOpenBundleResourceMap (bundleRef); const short resFileId = CFBundleOpenBundleResourceMap (bundleRef);
UseResFile (resFileId); UseResFile (resFileId);
for (ResourceIndex i = 1; i <= Count1Resources ('thng'); ++i) const OSType thngType = stringToOSType ("thng");
for (ResourceIndex i = 1; i <= Count1Resources (thngType); ++i)
{ {
if (Handle h = Get1IndResource ('thng', i)) if (Handle h = Get1IndResource (thngType, i))
{ {
HLock (h); HLock (h);
const uint32* const types = (const uint32*) *h; const uint32* const types = (const uint32*) *h;
@@ -346,11 +345,21 @@ public:
refreshParameterList(); refreshParameterList();
updateNumChannels(); updateNumChannels();
producesMidiMessages = canProduceMidiOutput(); producesMidiMessages = canProduceMidiOutput();
setPluginCallbacks();
setPlayConfigDetails (numInputBusChannels * numInputBusses, setPlayConfigDetails (numInputBusChannels * numInputBusses,
numOutputBusChannels * numOutputBusses, numOutputBusChannels * numOutputBusses,
rate, blockSize); rate, blockSize);
setLatencySamples (0); setLatencySamples (0);
if (parameters.size() == 0)
{
// some plugins crash if initialiseAudioUnit() is called too soon (sigh..), so we'll
// only call it here if it seems like they it's one of the awkward plugins that can
// only create their parameters after it has been initialised.
initialiseAudioUnit();
refreshParameterList();
}
setPluginCallbacks();
} }
//============================================================================== //==============================================================================
@@ -915,7 +924,7 @@ private:
bool automatable; bool automatable;
}; };
OwnedArray <ParamInfo> parameters; OwnedArray<ParamInfo> parameters;
MidiDataConcatenator midiConcatenator; MidiDataConcatenator midiConcatenator;
CriticalSection midiInLock; CriticalSection midiInLock;
@@ -972,16 +981,11 @@ private:
for (int i = 0; i < parameters.size(); ++i) for (int i = 0; i < parameters.size(); ++i)
{ {
const ParamInfo& p = *parameters.getUnchecked(i);
AudioUnitParameter paramToAdd;
paramToAdd.mAudioUnit = audioUnit;
paramToAdd.mParameterID = p.paramID;
paramToAdd.mScope = kAudioUnitScope_Global;
paramToAdd.mElement = 0;
AudioUnitEvent event; AudioUnitEvent event;
event.mArgument.mParameter = paramToAdd; event.mArgument.mParameter.mAudioUnit = audioUnit;
event.mArgument.mParameter.mParameterID = parameters.getUnchecked(i)->paramID;
event.mArgument.mParameter.mScope = kAudioUnitScope_Global;
event.mArgument.mParameter.mElement = 0;
event.mEventType = kAudioUnitEvent_ParameterValueChange; event.mEventType = kAudioUnitEvent_ParameterValueChange;
AUEventListenerAddEventType (eventListenerRef, nullptr, &event); AUEventListenerAddEventType (eventListenerRef, nullptr, &event);
@@ -992,6 +996,16 @@ private:
event.mEventType = kAudioUnitEvent_EndParameterChangeGesture; event.mEventType = kAudioUnitEvent_EndParameterChangeGesture;
AUEventListenerAddEventType (eventListenerRef, nullptr, &event); AUEventListenerAddEventType (eventListenerRef, nullptr, &event);
} }
// Add a listener for program changes
AudioUnitEvent event;
event.mArgument.mProperty.mAudioUnit = audioUnit;
event.mArgument.mProperty.mPropertyID = kAudioUnitProperty_PresentPreset;
event.mArgument.mProperty.mScope = kAudioUnitScope_Global;
event.mArgument.mProperty.mElement = 0;
event.mEventType = kAudioUnitEvent_PropertyChange;
AUEventListenerAddEventType (eventListenerRef, nullptr, &event);
} }
} }
@@ -1022,6 +1036,7 @@ private:
break; break;
default: default:
sendAllParametersChangedEvents();
break; break;
} }
} }
@@ -1600,7 +1615,7 @@ AudioUnitPluginFormat::~AudioUnitPluginFormat()
{ {
} }
void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray <PluginDescription>& results, void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray<PluginDescription>& results,
const String& fileOrIdentifier) const String& fileOrIdentifier)
{ {
if (! fileMightContainThisPluginType (fileOrIdentifier)) if (! fileMightContainThisPluginType (fileOrIdentifier))
@@ -118,8 +118,8 @@ class LADSPAPluginInstance : public AudioPluginInstance
{ {
public: public:
LADSPAPluginInstance (const LADSPAModuleHandle::Ptr& m) LADSPAPluginInstance (const LADSPAModuleHandle::Ptr& m)
: plugin (nullptr), handle (nullptr), initialised (false), : module (m), plugin (nullptr), handle (nullptr),
tempBuffer (1, 1), module (m) initialised (false), tempBuffer (1, 1)
{ {
++insideLADSPACallback; ++insideLADSPACallback;
@@ -85,6 +85,18 @@ static Steinberg::Vst::TChar* toString (const juce::String& source) noexcept
//============================================================================== //==============================================================================
static Steinberg::Vst::SpeakerArrangement getArrangementForBus (Steinberg::Vst::IAudioProcessor* processor,
bool isInput, int busIndex)
{
Steinberg::Vst::SpeakerArrangement arrangement = Steinberg::Vst::SpeakerArr::kEmpty;
if (processor != nullptr)
processor->getBusArrangement (isInput ? Steinberg::Vst::kInput : Steinberg::Vst::kOutput,
(Steinberg::int32) busIndex, arrangement);
return arrangement;
}
/** For the sake of simplicity, there can only be 1 arrangement type per channel count. /** For the sake of simplicity, there can only be 1 arrangement type per channel count.
i.e.: 4 channels == k31Cine OR k40Cine i.e.: 4 channels == k31Cine OR k40Cine
*/ */
@@ -126,9 +138,9 @@ class ComSmartPtr
{ {
public: public:
ComSmartPtr() noexcept : source (nullptr) {} ComSmartPtr() noexcept : source (nullptr) {}
ComSmartPtr (ObjectType* object) noexcept : source (object) { if (source != nullptr) source->addRef(); } ComSmartPtr (ObjectType* object, bool autoAddRef = true) noexcept : source (object) { if (source != nullptr && autoAddRef) source->addRef(); }
ComSmartPtr (const ComSmartPtr& other) noexcept : source (other.source) { if (source != nullptr) source->addRef(); } ComSmartPtr (const ComSmartPtr& other) noexcept : source (other.source) { if (source != nullptr) source->addRef(); }
~ComSmartPtr() { if (source != nullptr) source->release(); } ~ComSmartPtr() { if (source != nullptr) source->release(); }
operator ObjectType*() const noexcept { return source; } operator ObjectType*() const noexcept { return source; }
ObjectType* get() const noexcept { return source; } ObjectType* get() const noexcept { return source; }
@@ -341,7 +353,7 @@ namespace VST3BufferExchange
Bus& bus, Bus& bus,
AudioSampleBuffer& buffer, AudioSampleBuffer& buffer,
int numChannels, int channelStartOffset, int numChannels, int channelStartOffset,
int sampleOffset = 0) noexcept int sampleOffset = 0)
{ {
const int channelEnd = numChannels + channelStartOffset; const int channelEnd = numChannels + channelStartOffset;
jassert (channelEnd >= 0 && channelEnd <= buffer.getNumChannels()); jassert (channelEnd >= 0 && channelEnd <= buffer.getNumChannels());
@@ -356,37 +368,52 @@ namespace VST3BufferExchange
vstBuffers.silenceFlags = 0; vstBuffers.silenceFlags = 0;
} }
static void mapArrangementToBusses (int& channelIndexOffset, int index,
Array<Steinberg::Vst::AudioBusBuffers>& result,
BusMap& busMapToUse, Steinberg::Vst::SpeakerArrangement arrangement,
AudioSampleBuffer& source)
{
const int numChansForBus = BigInteger ((juce::int64) arrangement).countNumberOfSetBits();
if (index >= result.size())
result.add (Steinberg::Vst::AudioBusBuffers());
if (index >= busMapToUse.size())
busMapToUse.add (Bus());
if (numChansForBus > 0)
{
associateBufferTo (result.getReference (index),
busMapToUse.getReference (index),
source, numChansForBus, channelIndexOffset);
}
channelIndexOffset += numChansForBus;
}
static void mapBufferToBusses (Array<Steinberg::Vst::AudioBusBuffers>& result, BusMap& busMapToUse,
const Array<Steinberg::Vst::SpeakerArrangement>& arrangements,
AudioSampleBuffer& source)
{
int channelIndexOffset = 0;
for (int i = 0; i < arrangements.size(); ++i)
mapArrangementToBusses (channelIndexOffset, i, result, busMapToUse,
arrangements.getUnchecked (i), source);
}
static void mapBufferToBusses (Array<Steinberg::Vst::AudioBusBuffers>& result, static void mapBufferToBusses (Array<Steinberg::Vst::AudioBusBuffers>& result,
Steinberg::Vst::IAudioProcessor& processor, Steinberg::Vst::IAudioProcessor& processor,
BusMap& busMapToUse, BusMap& busMapToUse, bool isInput, int numBusses,
bool isInput, int numBusses,
AudioSampleBuffer& source) AudioSampleBuffer& source)
{ {
int channelIndexOffset = 0; int channelIndexOffset = 0;
for (int i = 0; i < numBusses; ++i) for (int i = 0; i < numBusses; ++i)
{ mapArrangementToBusses (channelIndexOffset, i,
Steinberg::Vst::SpeakerArrangement arrangement = 0; result, busMapToUse,
processor.getBusArrangement (isInput ? Steinberg::Vst::kInput : Steinberg::Vst::kOutput, getArrangementForBus (&processor, isInput, i),
(Steinberg::int32) i, arrangement); source);
const int numChansForBus = BigInteger ((juce::int64) arrangement).countNumberOfSetBits();
if (i >= result.size())
result.add (Steinberg::Vst::AudioBusBuffers());
if (i >= busMapToUse.size())
busMapToUse.add (Bus());
if (numChansForBus > 0)
{
associateBufferTo (result.getReference (i),
busMapToUse.getReference (i),
source, numChansForBus, channelIndexOffset);
}
channelIndexOffset += numChansForBus;
}
} }
} }
@@ -31,6 +31,7 @@
#define JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY 1 #define JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY 1
#endif #endif
#include <map>
#include "juce_VST3Headers.h" #include "juce_VST3Headers.h"
#undef JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY #undef JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY
@@ -111,6 +112,7 @@ static void createPluginDescription (PluginDescription& description,
description.lastFileModTime = pluginFile.getLastModificationTime(); description.lastFileModTime = pluginFile.getLastModificationTime();
description.manufacturerName = company; description.manufacturerName = company;
description.name = name; description.name = name;
description.descriptiveName = name;
description.pluginFormatName = "VST3"; description.pluginFormatName = "VST3";
description.numInputChannels = numInputs; description.numInputChannels = numInputs;
description.numOutputChannels = numOutputs; description.numOutputChannels = numOutputs;
@@ -378,32 +380,65 @@ public:
FUnknown* getFUnknown() { return static_cast<Vst::IComponentHandler*> (this); } FUnknown* getFUnknown() { return static_cast<Vst::IComponentHandler*> (this); }
static bool hasFlag (Steinberg::int32 source, Steinberg::int32 flag) noexcept
{
return (source & flag) == flag;
}
//============================================================================== //==============================================================================
tresult PLUGIN_API beginEdit (Vst::ParamID) override tresult PLUGIN_API beginEdit (Vst::ParamID paramID) override
{ {
// XXX todo.. const int index = getIndexOfParamID (paramID);
return kResultFalse;
if (index < 0)
return kResultFalse;
owner->beginParameterChangeGesture (index);
return kResultTrue;
} }
tresult PLUGIN_API performEdit (Vst::ParamID id, Vst::ParamValue valueNormalized) override tresult PLUGIN_API performEdit (Vst::ParamID paramID, Vst::ParamValue valueNormalized) override
{ {
if (owner != nullptr) const int index = getIndexOfParamID (paramID);
return owner->editController->setParamNormalized (id, valueNormalized);
return kResultFalse; if (index < 0)
return kResultFalse;
owner->sendParamChangeMessageToListeners (index, (float) valueNormalized);
return owner->editController->setParamNormalized (paramID, valueNormalized);
} }
tresult PLUGIN_API endEdit (Vst::ParamID) override tresult PLUGIN_API endEdit (Vst::ParamID paramID) override
{ {
// XXX todo.. const int index = getIndexOfParamID (paramID);
return kResultFalse;
if (index < 0)
return kResultFalse;
owner->endParameterChangeGesture (index);
return kResultTrue;
} }
tresult PLUGIN_API restartComponent (Steinberg::int32) override tresult PLUGIN_API restartComponent (Steinberg::int32 flags) override
{ {
if (owner != nullptr) if (owner != nullptr)
{ {
owner->reset(); if (hasFlag (flags, Vst::kReloadComponent))
owner->reset();
if (hasFlag (flags, Vst::kIoChanged))
{
const double sampleRate = owner->getSampleRate();
const int blockSize = owner->getBlockSize();
owner->prepareToPlay (sampleRate >= 8000 ? sampleRate : 44100.0,
blockSize > 0 ? blockSize : 1024);
}
if (hasFlag (flags, Vst::kLatencyChanged))
if (owner->processor != nullptr)
owner->setLatencySamples (jmax (0, (int) owner->processor->getLatencySamples()));
owner->updateHostDisplay(); owner->updateHostDisplay();
return kResultTrue; return kResultTrue;
} }
@@ -437,9 +472,171 @@ public:
return kResultFalse; return kResultFalse;
} }
//==============================================================================
class ContextMenu : public Vst::IContextMenu
{
public:
ContextMenu (VST3PluginInstance& pluginInstance) : owner (pluginInstance) {}
virtual ~ContextMenu() {}
JUCE_DECLARE_VST3_COM_REF_METHODS
JUCE_DECLARE_VST3_COM_QUERY_METHODS
Steinberg::int32 PLUGIN_API getItemCount() override { return (Steinberg::int32) items.size(); }
tresult PLUGIN_API addItem (const Item& item, IContextMenuTarget* target) override
{
jassert (target != nullptr);
ItemAndTarget newItem;
newItem.item = item;
newItem.target = target;
items.add (newItem);
return kResultOk;
}
tresult PLUGIN_API removeItem (const Item& toRemove, IContextMenuTarget* target) override
{
for (int i = items.size(); --i >= 0;)
{
ItemAndTarget& item = items.getReference(i);
if (item.item.tag == toRemove.tag && item.target == target)
items.remove (i);
}
return kResultOk;
}
tresult PLUGIN_API getItem (Steinberg::int32 tag, Item& result, IContextMenuTarget** target) override
{
for (int i = 0; i < items.size(); ++i)
{
const ItemAndTarget& item = items.getReference(i);
if (item.item.tag == tag)
{
result = item.item;
if (target != nullptr)
*target = item.target;
return kResultTrue;
}
}
zerostruct (result);
return kResultFalse;
}
tresult PLUGIN_API popup (Steinberg::UCoord x, Steinberg::UCoord y) override
{
Array<const Item*> subItemStack;
OwnedArray<PopupMenu> menuStack;
PopupMenu* topLevelMenu = menuStack.add (new PopupMenu());
for (int i = 0; i < items.size(); ++i)
{
const Item& item = items.getReference (i).item;
PopupMenu* menuToUse = menuStack.getLast();
if (hasFlag (item.flags, Item::kIsGroupStart & ~Item::kIsDisabled))
{
subItemStack.add (&item);
menuStack.add (new PopupMenu());
}
else if (hasFlag (item.flags, Item::kIsGroupEnd))
{
if (const Item* subItem = subItemStack.getLast())
{
if (PopupMenu* m = menuStack [menuStack.size() - 2])
m->addSubMenu (toString (subItem->name), *menuToUse,
! hasFlag (subItem->flags, Item::kIsDisabled),
nullptr,
hasFlag (subItem->flags, Item::kIsChecked));
menuStack.removeLast (1);
subItemStack.removeLast (1);
}
}
else if (hasFlag (item.flags, Item::kIsSeparator))
{
menuToUse->addSeparator();
}
else
{
menuToUse->addItem (item.tag != 0 ? (int) item.tag : (int) zeroTagReplacement,
toString (item.name),
! hasFlag (item.flags, Item::kIsDisabled),
hasFlag (item.flags, Item::kIsChecked));
}
}
PopupMenu::Options options;
if (AudioProcessorEditor* ed = owner.getActiveEditor())
options = options.withTargetScreenArea (ed->getScreenBounds().translated ((int) x, (int) y).withSize (1, 1));
#if JUCE_MODAL_LOOPS_PERMITTED
// Unfortunately, Steinberg's docs explicitly say this should be modal..
handleResult (topLevelMenu->showMenu (options));
#else
topLevelMenu->showMenuAsync (options, ModalCallbackFunction::create (menuFinished, ComSmartPtr<ContextMenu> (this)));
#endif
return kResultOk;
}
#if ! JUCE_MODAL_LOOPS_PERMITTED
static void menuFinished (int modalResult, ComSmartPtr<ContextMenu> menu) { menu->handleResult (modalResult); }
#endif
private:
enum { zeroTagReplacement = 0x7fffffff };
Atomic<int> refCount;
VST3PluginInstance& owner;
struct ItemAndTarget
{
Item item;
ComSmartPtr<IContextMenuTarget> target;
};
Array<ItemAndTarget> items;
void handleResult (int result)
{
if (result == 0)
return;
if (result == zeroTagReplacement)
result = 0;
for (int i = 0; i < items.size(); ++i)
{
const ItemAndTarget& item = items.getReference(i);
if ((int) item.item.tag == result)
{
if (item.target != nullptr)
item.target->executeMenuItem ((Steinberg::int32) result);
break;
}
}
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContextMenu)
};
Vst::IContextMenu* PLUGIN_API createContextMenu (IPlugView*, const Vst::ParamID*) override Vst::IContextMenu* PLUGIN_API createContextMenu (IPlugView*, const Vst::ParamID*) override
{ {
jassertfalse; if (owner != nullptr)
return new ContextMenu (*owner);
return nullptr; return nullptr;
} }
@@ -549,9 +746,42 @@ public:
private: private:
//============================================================================== //==============================================================================
Atomic<int32> refCount; VST3PluginInstance* const owner;
Atomic<int> refCount;
String appName; String appName;
VST3PluginInstance* owner;
typedef std::map<Vst::ParamID, int> ParamMapType;
ParamMapType paramToIndexMap;
int getIndexOfParamID (Vst::ParamID paramID)
{
if (owner == nullptr || owner->editController == nullptr)
return -1;
int result = getMappedParamID (paramID);
if (result < 0)
{
const int numParams = owner->editController->getParameterCount();
for (int i = 0; i < numParams; ++i)
{
Vst::ParameterInfo paramInfo;
owner->editController->getParameterInfo (i, paramInfo);
paramToIndexMap[paramInfo.id] = i;
}
result = getMappedParamID (paramID);
}
return result;
}
int getMappedParamID (Vst::ParamID paramID)
{
const ParamMapType::iterator it (paramToIndexMap.find (paramID));
return it != paramToIndexMap.end() ? it->second : -1;
}
//============================================================================== //==============================================================================
class Message : public Vst::IMessage class Message : public Vst::IMessage
@@ -577,9 +807,9 @@ private:
JUCE_DECLARE_VST3_COM_REF_METHODS JUCE_DECLARE_VST3_COM_REF_METHODS
JUCE_DECLARE_VST3_COM_QUERY_METHODS JUCE_DECLARE_VST3_COM_QUERY_METHODS
FIDString PLUGIN_API getMessageID() { return messageId.toRawUTF8(); } FIDString PLUGIN_API getMessageID() override { return messageId.toRawUTF8(); }
void PLUGIN_API setMessageID (FIDString id) { messageId = toString (id); } void PLUGIN_API setMessageID (FIDString id) override { messageId = toString (id); }
Vst::IAttributeList* PLUGIN_API getAttributes() { return attributeList; } Vst::IAttributeList* PLUGIN_API getAttributes() override { return attributeList; }
var value; var value;
@@ -587,7 +817,7 @@ private:
VST3HostContext& owner; VST3HostContext& owner;
ComSmartPtr<Vst::IAttributeList> attributeList; ComSmartPtr<Vst::IAttributeList> attributeList;
String messageId; String messageId;
Atomic<int32> refCount; Atomic<int> refCount;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Message) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Message)
}; };
@@ -715,7 +945,7 @@ private:
private: private:
VST3HostContext* owner; VST3HostContext* owner;
Atomic<int32> refCount; Atomic<int> refCount;
//============================================================================== //==============================================================================
template<typename Type> template<typename Type>
@@ -1195,7 +1425,8 @@ public:
VST3PluginWindow (AudioProcessor* owner, IPlugView* pluginView) VST3PluginWindow (AudioProcessor* owner, IPlugView* pluginView)
: AudioProcessorEditor (owner), : AudioProcessorEditor (owner),
ComponentMovementWatcher (this), ComponentMovementWatcher (this),
view (pluginView), refCount (1),
view (pluginView, false),
pluginHandle (nullptr), pluginHandle (nullptr),
recursiveResize (false) recursiveResize (false)
{ {
@@ -1213,7 +1444,9 @@ public:
~VST3PluginWindow() ~VST3PluginWindow()
{ {
warnOnFailure (view->removed()); warnOnFailure (view->removed());
getAudioProcessor()->editorBeingDeleted (this); warnOnFailure (view->setFrame (nullptr));
processor.editorBeingDeleted (this);
#if JUCE_MAC #if JUCE_MAC
dummyComponent.setView (nullptr); dummyComponent.setView (nullptr);
@@ -1266,20 +1499,32 @@ public:
{ {
rect.right = (Steinberg::int32) getWidth(); rect.right = (Steinberg::int32) getWidth();
rect.bottom = (Steinberg::int32) getHeight(); rect.bottom = (Steinberg::int32) getHeight();
view->checkSizeConstraint (&rect);
setSize ((int) rect.getWidth(), (int) rect.getHeight());
#if JUCE_WINDOWS
SetWindowPos (pluginHandle, 0,
pos.x, pos.y, rect.getWidth(), rect.getHeight(),
isVisible() ? SWP_SHOWWINDOW : SWP_HIDEWINDOW);
#elif JUCE_MAC
dummyComponent.setBounds (getLocalBounds());
#endif
view->onSize (&rect); view->onSize (&rect);
} }
else else
{ {
warnOnFailure (view->getSize (&rect)); warnOnFailure (view->getSize (&rect));
}
#if JUCE_WINDOWS #if JUCE_WINDOWS
SetWindowPos (pluginHandle, 0, SetWindowPos (pluginHandle, 0,
pos.x, pos.y, rect.getWidth(), rect.getHeight(), pos.x, pos.y, rect.getWidth(), rect.getHeight(),
isVisible() ? SWP_SHOWWINDOW : SWP_HIDEWINDOW); isVisible() ? SWP_SHOWWINDOW : SWP_HIDEWINDOW);
#elif JUCE_MAC #elif JUCE_MAC
dummyComponent.setBounds (0, 0, (int) rect.getWidth(), (int) rect.getHeight()); dummyComponent.setBounds (0, 0, (int) rect.getWidth(), (int) rect.getHeight());
#endif #endif
}
// Some plugins don't update their cursor correctly when mousing out the window // Some plugins don't update their cursor correctly when mousing out the window
Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate(); Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
@@ -1364,7 +1609,7 @@ private:
if (peer != nullptr) if (peer != nullptr)
pluginHandle = (HandleFormat) peer->getNativeHandle(); pluginHandle = (HandleFormat) peer->getNativeHandle();
#elif JUCE_MAC #elif JUCE_MAC
dummyComponent.setBounds (getBounds().withZeroOrigin()); dummyComponent.setBounds (getLocalBounds());
addAndMakeVisible (dummyComponent); addAndMakeVisible (dummyComponent);
pluginHandle = (NSView*) dummyComponent.getView(); pluginHandle = (NSView*) dummyComponent.getView();
jassert (pluginHandle != nil); jassert (pluginHandle != nil);
@@ -1391,7 +1636,8 @@ public:
midiInputs (new MidiEventList()), midiInputs (new MidiEventList()),
midiOutputs (new MidiEventList()), midiOutputs (new MidiEventList()),
isComponentInitialised (false), isComponentInitialised (false),
isControllerInitialised (false) isControllerInitialised (false),
isActive (false)
{ {
host = new VST3HostContext (this); host = new VST3HostContext (this);
} }
@@ -1413,7 +1659,6 @@ public:
if (isControllerInitialised) editController->terminate(); if (isControllerInitialised) editController->terminate();
if (isComponentInitialised) component->terminate(); if (isComponentInitialised) component->terminate();
//Deletion order appears to matter:
componentConnection = nullptr; componentConnection = nullptr;
editControllerConnection = nullptr; editControllerConnection = nullptr;
unitData = nullptr; unitData = nullptr;
@@ -1425,6 +1670,8 @@ public:
editController2 = nullptr; editController2 = nullptr;
editController = nullptr; editController = nullptr;
component = nullptr; component = nullptr;
host = nullptr;
module = nullptr;
} }
bool initialise() bool initialise()
@@ -1478,8 +1725,27 @@ public:
return module != nullptr ? module->name : String::empty; return module != nullptr ? module->name : String::empty;
} }
void repopulateArrangements()
{
inputArrangements.clearQuick();
outputArrangements.clearQuick();
// NB: Some plugins need a valid arrangement despite specifying 0 for their I/O busses
for (int i = 0; i < jmax (1, numInputAudioBusses); ++i)
inputArrangements.add (getArrangementForBus (processor, true, i));
for (int i = 0; i < jmax (1, numOutputAudioBusses); ++i)
outputArrangements.add (getArrangementForBus (processor, false, i));
}
void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock) override void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock) override
{ {
// Avoid redundantly calling things like setActive, which can be a heavy-duty call for some plugins:
if (isActive
&& getSampleRate() == sampleRate
&& getBlockSize() == estimatedSamplesPerBlock)
return;
using namespace Vst; using namespace Vst;
ProcessSetup setup; ProcessSetup setup;
@@ -1495,20 +1761,25 @@ public:
editController->setComponentHandler (host); editController->setComponentHandler (host);
Array<SpeakerArrangement> inArrangements, outArrangements; if (inputArrangements.size() <= 0 || outputArrangements.size() <= 0)
repopulateArrangements();
for (int i = 0; i < numInputAudioBusses; ++i) warnOnFailure (processor->setBusArrangements (inputArrangements.getRawDataPointer(), numInputAudioBusses,
inArrangements.add (getArrangementForNumChannels (jmax (0, (int) getBusInfo (true, true, i).channelCount))); outputArrangements.getRawDataPointer(), numOutputAudioBusses));
for (int i = 0; i < numOutputAudioBusses; ++i)
outArrangements.add (getArrangementForNumChannels (jmax (0, (int) getBusInfo (false, true, i).channelCount)));
warnOnFailure (processor->setBusArrangements (inArrangements.getRawDataPointer(), numInputAudioBusses,
outArrangements.getRawDataPointer(), numOutputAudioBusses));
// Update the num. busses in case the configuration has been modified by the plugin. (May affect number of channels!): // Update the num. busses in case the configuration has been modified by the plugin. (May affect number of channels!):
numInputAudioBusses = getNumSingleDirectionBussesFor (component, true, true); const int newNumInputAudioBusses = getNumSingleDirectionBussesFor (component, true, true);
numOutputAudioBusses = getNumSingleDirectionBussesFor (component, false, true); const int newNumOutputAudioBusses = getNumSingleDirectionBussesFor (component, false, true);
// Repopulate arrangements if the number of busses have changed:
if (numInputAudioBusses != newNumInputAudioBusses
|| numOutputAudioBusses != newNumOutputAudioBusses)
{
numInputAudioBusses = newNumInputAudioBusses;
numOutputAudioBusses = newNumOutputAudioBusses;
repopulateArrangements();
}
// Needed for having the same sample rate in processBlock(); some plugins need this! // Needed for having the same sample rate in processBlock(); some plugins need this!
setPlayConfigDetails (getNumSingleDirectionChannelsFor (component, true, true), setPlayConfigDetails (getNumSingleDirectionChannelsFor (component, true, true),
@@ -1517,21 +1788,30 @@ public:
setStateForAllBusses (true); setStateForAllBusses (true);
setLatencySamples (jmax (0, (int) processor->getLatencySamples()));
warnOnFailure (component->setActive (true)); warnOnFailure (component->setActive (true));
warnOnFailure (processor->setProcessing (true)); warnOnFailure (processor->setProcessing (true));
isActive = true;
} }
void releaseResources() override void releaseResources() override
{ {
if (! isActive)
return; // Avoids redundantly calling things like setActive
JUCE_TRY JUCE_TRY
{ {
isActive = false;
setStateForAllBusses (false); setStateForAllBusses (false);
if (processor != nullptr) if (processor != nullptr)
processor->setProcessing (false); warnOnFailure (processor->setProcessing (false));
if (component != nullptr) if (component != nullptr)
component->setActive (false); warnOnFailure (component->setActive (false));
} }
JUCE_CATCH_ALL_ASSERT JUCE_CATCH_ALL_ASSERT
} }
@@ -1540,7 +1820,8 @@ public:
{ {
using namespace Vst; using namespace Vst;
if (processor != nullptr if (isActive
&& processor != nullptr
&& processor->canProcessSampleSize (kSample32) == kResultTrue) && processor->canProcessSampleSize (kSample32) == kResultTrue)
{ {
const int numSamples = buffer.getNumSamples(); const int numSamples = buffer.getNumSamples();
@@ -1612,15 +1893,22 @@ public:
//============================================================================== //==============================================================================
bool silenceInProducesSilenceOut() const override bool silenceInProducesSilenceOut() const override
{ {
return processor == nullptr; if (processor != nullptr)
return processor->getTailSamples() == Vst::kNoTail;
return true;
} }
/** May return a negative value as a means of informing us that the plugin has "infinite tail," or 0 for "no tail." */ /** May return a negative value as a means of informing us that the plugin has "infinite tail," or 0 for "no tail." */
double getTailLengthSeconds() const override double getTailLengthSeconds() const override
{ {
if (processor != nullptr) if (processor != nullptr)
return (double) jmin ((int) jmax ((Steinberg::uint32) 0, processor->getTailSamples()), 0x7fffffff) {
* getSampleRate(); const double sampleRate = getSampleRate();
if (sampleRate > 0.0)
return jlimit (0, 0x7fffffff, (int) processor->getTailSamples()) / sampleRate;
}
return 0.0; return 0.0;
} }
@@ -1640,7 +1928,7 @@ public:
if (getActiveEditor() != nullptr) if (getActiveEditor() != nullptr)
return true; return true;
ComSmartPtr<IPlugView> view (tryCreatingView()); ComSmartPtr<IPlugView> view (tryCreatingView(), false);
return view != nullptr; return view != nullptr;
} }
@@ -1706,6 +1994,16 @@ public:
return toString (result); return toString (result);
} }
//==============================================================================
void reset() override
{
if (component != nullptr)
{
component->setActive (false);
component->setActive (true);
}
}
//============================================================================== //==============================================================================
void getStateInformation (MemoryBlock& destData) override void getStateInformation (MemoryBlock& destData) override
{ {
@@ -1785,6 +2083,7 @@ private:
as very poorly specified by the Steinberg SDK as very poorly specified by the Steinberg SDK
*/ */
int numInputAudioBusses, numOutputAudioBusses; int numInputAudioBusses, numOutputAudioBusses;
Array<Vst::SpeakerArrangement> inputArrangements, outputArrangements; // Caching to improve performance and to avoid possible non-thread-safe calls to getBusArrangements().
VST3BufferExchange::BusMap inputBusMap, outputBusMap; VST3BufferExchange::BusMap inputBusMap, outputBusMap;
Array<Vst::AudioBusBuffers> inputBusses, outputBusses; Array<Vst::AudioBusBuffers> inputBusses, outputBusses;
@@ -1813,7 +2112,11 @@ private:
MemoryBlock mem; MemoryBlock mem;
if (mem.fromBase64Encoding (state->getAllSubText())) if (mem.fromBase64Encoding (state->getAllSubText()))
stream = new Steinberg::MemoryStream (mem.getData(), (TSize) mem.getSize()); {
stream = new Steinberg::MemoryStream();
stream->setSize ((TSize) mem.getSize());
mem.copyTo (stream->getData(), 0, mem.getSize());
}
} }
return stream; return stream;
@@ -1829,21 +2132,21 @@ private:
JUCE_DECLARE_VST3_COM_REF_METHODS JUCE_DECLARE_VST3_COM_REF_METHODS
JUCE_DECLARE_VST3_COM_QUERY_METHODS JUCE_DECLARE_VST3_COM_QUERY_METHODS
Steinberg::int32 PLUGIN_API getParameterCount() { return 0; } Steinberg::int32 PLUGIN_API getParameterCount() override { return 0; }
Vst::IParamValueQueue* PLUGIN_API getParameterData (Steinberg::int32) Vst::IParamValueQueue* PLUGIN_API getParameterData (Steinberg::int32) override
{ {
return nullptr; return nullptr;
} }
Vst::IParamValueQueue* PLUGIN_API addParameterData (const Vst::ParamID&, Steinberg::int32& index) Vst::IParamValueQueue* PLUGIN_API addParameterData (const Vst::ParamID&, Steinberg::int32& index) override
{ {
index = 0; index = 0;
return nullptr; return nullptr;
} }
private: private:
Atomic<int32> refCount; Atomic<int> refCount;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParameterChangeList) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParameterChangeList)
}; };
@@ -1851,7 +2154,7 @@ private:
ComSmartPtr<ParameterChangeList> inputParameterChanges, outputParameterChanges; ComSmartPtr<ParameterChangeList> inputParameterChanges, outputParameterChanges;
ComSmartPtr<MidiEventList> midiInputs, midiOutputs; ComSmartPtr<MidiEventList> midiInputs, midiOutputs;
Vst::ProcessContext timingInfo; //< Only use this in processBlock()! Vst::ProcessContext timingInfo; //< Only use this in processBlock()!
bool isComponentInitialised, isControllerInitialised; bool isComponentInitialised, isControllerInitialised, isActive;
//============================================================================== //==============================================================================
bool fetchComponentAndController (IPluginFactory* factory, const Steinberg::int32 numClasses) bool fetchComponentAndController (IPluginFactory* factory, const Steinberg::int32 numClasses)
@@ -2031,11 +2334,11 @@ private:
Vst::BusInfo getBusInfo (bool forInput, bool forAudio, int index = 0) const Vst::BusInfo getBusInfo (bool forInput, bool forAudio, int index = 0) const
{ {
Vst::BusInfo busInfo; Vst::BusInfo busInfo;
busInfo.mediaType = forAudio ? Vst::kAudio : Vst::kEvent;
busInfo.direction = forInput ? Vst::kInput : Vst::kOutput;
component->getBusInfo (forAudio ? Vst::kAudio : Vst::kEvent, component->getBusInfo (busInfo.mediaType, busInfo.direction,
forInput ? Vst::kInput : Vst::kOutput,
(Steinberg::int32) index, busInfo); (Steinberg::int32) index, busInfo);
return busInfo; return busInfo;
} }
@@ -2056,11 +2359,8 @@ private:
{ {
using namespace VST3BufferExchange; using namespace VST3BufferExchange;
mapBufferToBusses (inputBusses, *processor, inputBusMap, mapBufferToBusses (inputBusses, inputBusMap, inputArrangements, buffer);
true, numInputAudioBusses, buffer); mapBufferToBusses (outputBusses, outputBusMap, outputArrangements, buffer);
mapBufferToBusses (outputBusses, *processor, outputBusMap,
false, numOutputAudioBusses, buffer);
destination.inputs = inputBusses.getRawDataPointer(); destination.inputs = inputBusses.getRawDataPointer();
destination.outputs = outputBusses.getRawDataPointer(); destination.outputs = outputBusses.getRawDataPointer();
@@ -29,13 +29,6 @@
#include "../../juce_gui_extra/native/juce_mac_CarbonViewWrapperComponent.h" #include "../../juce_gui_extra/native/juce_mac_CarbonViewWrapperComponent.h"
#endif #endif
#if JUCE_MAC
static bool makeFSRefFromPath (FSRef* destFSRef, const String& path)
{
return FSPathMakeRef (reinterpret_cast<const UInt8*> (path.toRawUTF8()), destFSRef, 0) == noErr;
}
#endif
//============================================================================== //==============================================================================
#undef PRAGMA_ALIGN_SUPPORTED #undef PRAGMA_ALIGN_SUPPORTED
#define VST_FORCE_DEPRECATED 0 #define VST_FORCE_DEPRECATED 0
@@ -84,74 +77,68 @@
#endif #endif
//============================================================================== //==============================================================================
const int fxbVersionNum = 1;
struct fxProgram
{
VstInt32 chunkMagic; // 'CcnK'
VstInt32 byteSize; // of this chunk, excl. magic + byteSize
VstInt32 fxMagic; // 'FxCk'
VstInt32 version;
VstInt32 fxID; // fx unique id
VstInt32 fxVersion;
VstInt32 numParams;
char prgName[28];
float params[1]; // variable no. of parameters
};
struct fxSet
{
VstInt32 chunkMagic; // 'CcnK'
VstInt32 byteSize; // of this chunk, excl. magic + byteSize
VstInt32 fxMagic; // 'FxBk'
VstInt32 version;
VstInt32 fxID; // fx unique id
VstInt32 fxVersion;
VstInt32 numPrograms;
char future[128];
fxProgram programs[1]; // variable no. of programs
};
struct fxChunkSet
{
VstInt32 chunkMagic; // 'CcnK'
VstInt32 byteSize; // of this chunk, excl. magic + byteSize
VstInt32 fxMagic; // 'FxCh', 'FPCh', or 'FBCh'
VstInt32 version;
VstInt32 fxID; // fx unique id
VstInt32 fxVersion;
VstInt32 numPrograms;
char future[128];
VstInt32 chunkSize;
char chunk[8]; // variable
};
struct fxProgramSet
{
VstInt32 chunkMagic; // 'CcnK'
VstInt32 byteSize; // of this chunk, excl. magic + byteSize
VstInt32 fxMagic; // 'FxCh', 'FPCh', or 'FBCh'
VstInt32 version;
VstInt32 fxID; // fx unique id
VstInt32 fxVersion;
VstInt32 numPrograms;
char name[28];
VstInt32 chunkSize;
char chunk[8]; // variable
};
namespace namespace
{ {
VstInt32 vst_swap (const VstInt32 x) noexcept const int fxbVersionNum = 1;
{
#ifdef JUCE_LITTLE_ENDIAN
return (VstInt32) ByteOrder::swap ((uint32) x);
#else
return x;
#endif
}
float vst_swapFloat (const float x) noexcept struct fxProgram
{
VstInt32 chunkMagic; // 'CcnK'
VstInt32 byteSize; // of this chunk, excl. magic + byteSize
VstInt32 fxMagic; // 'FxCk'
VstInt32 version;
VstInt32 fxID; // fx unique id
VstInt32 fxVersion;
VstInt32 numParams;
char prgName[28];
float params[1]; // variable no. of parameters
};
struct fxSet
{
VstInt32 chunkMagic; // 'CcnK'
VstInt32 byteSize; // of this chunk, excl. magic + byteSize
VstInt32 fxMagic; // 'FxBk'
VstInt32 version;
VstInt32 fxID; // fx unique id
VstInt32 fxVersion;
VstInt32 numPrograms;
char future[128];
fxProgram programs[1]; // variable no. of programs
};
struct fxChunkSet
{
VstInt32 chunkMagic; // 'CcnK'
VstInt32 byteSize; // of this chunk, excl. magic + byteSize
VstInt32 fxMagic; // 'FxCh', 'FPCh', or 'FBCh'
VstInt32 version;
VstInt32 fxID; // fx unique id
VstInt32 fxVersion;
VstInt32 numPrograms;
char future[128];
VstInt32 chunkSize;
char chunk[8]; // variable
};
struct fxProgramSet
{
VstInt32 chunkMagic; // 'CcnK'
VstInt32 byteSize; // of this chunk, excl. magic + byteSize
VstInt32 fxMagic; // 'FxCh', 'FPCh', or 'FBCh'
VstInt32 version;
VstInt32 fxID; // fx unique id
VstInt32 fxVersion;
VstInt32 numPrograms;
char name[28];
VstInt32 chunkSize;
char chunk[8]; // variable
};
static VstInt32 fxbName (const char* name) noexcept { return (VstInt32) ByteOrder::bigEndianInt (name); }
static VstInt32 fxbSwap (const VstInt32 x) noexcept { return (VstInt32) ByteOrder::swapIfLittleEndian ((uint32) x); }
static float fxbSwapFloat (const float x) noexcept
{ {
#ifdef JUCE_LITTLE_ENDIAN #ifdef JUCE_LITTLE_ENDIAN
union { uint32 asInt; float asFloat; } n; union { uint32 asInt; float asFloat; } n;
@@ -162,8 +149,12 @@ namespace
return x; return x;
#endif #endif
} }
}
double getVSTHostTimeNanoseconds() //==============================================================================
namespace
{
static double getVSTHostTimeNanoseconds() noexcept
{ {
#if JUCE_WINDOWS #if JUCE_WINDOWS
return timeGetTime() * 1000000.0; return timeGetTime() * 1000000.0;
@@ -177,40 +168,52 @@ namespace
return micro.lo * 1000.0; return micro.lo * 1000.0;
#endif #endif
} }
static int shellUIDToCreate = 0;
static int insideVSTCallback = 0;
struct IdleCallRecursionPreventer
{
IdleCallRecursionPreventer() : isMessageThread (MessageManager::getInstance()->isThisTheMessageThread())
{
if (isMessageThread)
++insideVSTCallback;
}
~IdleCallRecursionPreventer()
{
if (isMessageThread)
--insideVSTCallback;
}
const bool isMessageThread;
JUCE_DECLARE_NON_COPYABLE (IdleCallRecursionPreventer)
};
#if JUCE_MAC
static bool makeFSRefFromPath (FSRef* destFSRef, const String& path)
{
return FSPathMakeRef (reinterpret_cast<const UInt8*> (path.toRawUTF8()), destFSRef, 0) == noErr;
}
#endif
#if JUCE_MAC && JUCE_PPC
static void* newCFMFromMachO (void* const machofp) noexcept
{
void* result = (void*) new char[8];
((void**) result)[0] = machofp;
((void**) result)[1] = result;
return result;
}
#endif
} }
//============================================================================== //==============================================================================
typedef AEffect* (VSTCALLBACK *MainCall) (audioMasterCallback); typedef AEffect* (VSTCALLBACK *MainCall) (audioMasterCallback);
static VstIntPtr VSTCALLBACK audioMaster (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt); static VstIntPtr VSTCALLBACK audioMaster (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt);
static int shellUIDToCreate = 0;
static int insideVSTCallback = 0;
class IdleCallRecursionPreventer
{
public:
IdleCallRecursionPreventer()
: isMessageThread (MessageManager::getInstance()->isThisTheMessageThread())
{
if (isMessageThread)
++insideVSTCallback;
}
~IdleCallRecursionPreventer()
{
if (isMessageThread)
--insideVSTCallback;
}
private:
const bool isMessageThread;
JUCE_DECLARE_NON_COPYABLE (IdleCallRecursionPreventer)
};
class VSTPluginWindow;
//============================================================================== //==============================================================================
// Change this to disable logging of various VST activities // Change this to disable logging of various VST activities
#ifndef VST_LOGGING #ifndef VST_LOGGING
@@ -223,27 +226,12 @@ class VSTPluginWindow;
#define JUCE_VST_LOG(a) #define JUCE_VST_LOG(a)
#endif #endif
//==============================================================================
#if JUCE_MAC && JUCE_PPC
static void* NewCFMFromMachO (void* const machofp) noexcept
{
void* result = (void*) new char[8];
((void**) result)[0] = machofp;
((void**) result)[1] = result;
return result;
}
#endif
//============================================================================== //==============================================================================
#if JUCE_LINUX #if JUCE_LINUX
extern Display* display; extern Display* display;
extern XContext windowHandleXContext; extern XContext windowHandleXContext;
typedef void (*EventProcPtr) (XEvent* ev);
namespace namespace
{ {
static bool xErrorTriggered = false; static bool xErrorTriggered = false;
@@ -254,6 +242,8 @@ namespace
return 0; return 0;
} }
typedef void (*EventProcPtr) (XEvent* ev);
static EventProcPtr getPropertyFromXWindow (Window handle, Atom atom) static EventProcPtr getPropertyFromXWindow (Window handle, Atom atom)
{ {
XErrorHandler oldErrorHandler = XSetErrorHandler (temporaryErrorHandler); XErrorHandler oldErrorHandler = XSetErrorHandler (temporaryErrorHandler);
@@ -269,8 +259,7 @@ namespace
XSetErrorHandler (oldErrorHandler); XSetErrorHandler (oldErrorHandler);
return (userCount == 1 && ! xErrorTriggered) ? *reinterpret_cast<EventProcPtr*> (data) return (userCount == 1 && ! xErrorTriggered) ? *reinterpret_cast<EventProcPtr*> (data) : nullptr;
: 0;
} }
Window getChildWindow (Window windowToCheck) Window getChildWindow (Window windowToCheck)
@@ -279,12 +268,7 @@ namespace
Window* childWindows; Window* childWindows;
unsigned int numChildren = 0; unsigned int numChildren = 0;
XQueryTree (display, XQueryTree (display, windowToCheck, &rootWindow, &parentWindow, &childWindows, &numChildren);
windowToCheck,
&rootWindow,
&parentWindow,
&childWindows,
&numChildren);
if (numChildren > 0) if (numChildren > 0)
return childWindows [0]; return childWindows [0];
@@ -354,9 +338,9 @@ public:
typedef ReferenceCountedObjectPtr<ModuleHandle> Ptr; typedef ReferenceCountedObjectPtr<ModuleHandle> Ptr;
static Array <ModuleHandle*>& getActiveModules() static Array<ModuleHandle*>& getActiveModules()
{ {
static Array <ModuleHandle*> activeModules; static Array<ModuleHandle*> activeModules;
return activeModules; return activeModules;
} }
@@ -470,7 +454,7 @@ public:
{ {
if (HGLOBAL hGlob = LoadResource (dllModule, res)) if (HGLOBAL hGlob = LoadResource (dllModule, res))
{ {
const char* data = static_cast <const char*> (LockResource (hGlob)); const char* data = static_cast<const char*> (LockResource (hGlob));
return String::fromUTF8 (data, SizeofResource (dllModule, res)); return String::fromUTF8 (data, SizeofResource (dllModule, res));
} }
} }
@@ -552,7 +536,7 @@ public:
} }
} }
} }
#if JUCE_PPC #if JUCE_PPC
else else
{ {
FSRef fn; FSRef fn;
@@ -605,7 +589,7 @@ public:
} }
} }
} }
#endif #endif
return ok; return ok;
} }
@@ -683,7 +667,7 @@ public:
static void disposeMachOFromCFM (void* ptr) static void disposeMachOFromCFM (void* ptr)
{ {
delete[] static_cast <UInt32*> (ptr); delete[] static_cast<UInt32*> (ptr);
} }
void coerceAEffectFunctionCalls (AEffect* eff) void coerceAEffectFunctionCalls (AEffect* eff)
@@ -715,11 +699,11 @@ class VSTPluginInstance : public AudioPluginInstance,
private AsyncUpdater private AsyncUpdater
{ {
public: public:
VSTPluginInstance (const ModuleHandle::Ptr& module_) VSTPluginInstance (const ModuleHandle::Ptr& mh)
: effect (nullptr), : effect (nullptr),
module (module_), module (mh),
usesCocoaNSView (false), usesCocoaNSView (false),
name (module_->pluginName), name (mh->pluginName),
wantsMidiMessages (false), wantsMidiMessages (false),
initialised (false), initialised (false),
isPowerOn (false), isPowerOn (false),
@@ -741,7 +725,7 @@ public:
{ {
static void* audioMasterCoerced = nullptr; static void* audioMasterCoerced = nullptr;
if (audioMasterCoerced == nullptr) if (audioMasterCoerced == nullptr)
audioMasterCoerced = NewCFMFromMachO ((void*) &audioMaster); audioMasterCoerced = newCFMFromMachO ((void*) &audioMaster);
effect = module->moduleMain ((audioMasterCallback) audioMasterCoerced); effect = module->moduleMain ((audioMasterCallback) audioMasterCoerced);
} }
@@ -803,7 +787,7 @@ public:
char buffer [512] = { 0 }; char buffer [512] = { 0 };
dispatch (effGetEffectName, 0, 0, buffer, 0); dispatch (effGetEffectName, 0, 0, buffer, 0);
desc.descriptiveName = String (buffer).trim(); desc.descriptiveName = String::fromUTF8 (buffer).trim();
if (desc.descriptiveName.isEmpty()) if (desc.descriptiveName.isEmpty())
desc.descriptiveName = name; desc.descriptiveName = name;
@@ -818,7 +802,7 @@ public:
{ {
char buffer [kVstMaxVendorStrLen + 8] = { 0 }; char buffer [kVstMaxVendorStrLen + 8] = { 0 };
dispatch (effGetVendorString, 0, 0, buffer, 0); dispatch (effGetVendorString, 0, 0, buffer, 0);
desc.manufacturerName = buffer; desc.manufacturerName = String::fromUTF8 (buffer);
} }
desc.version = getVersion(); desc.version = getVersion();
@@ -1146,8 +1130,7 @@ public:
bool isValidChannel (int index, bool isInput) const bool isValidChannel (int index, bool isInput) const
{ {
return isInput ? (index < getNumInputChannels()) return isPositiveAndBelow (index, isInput ? getNumInputChannels() : getNumOutputChannels());
: (index < getNumOutputChannels());
} }
//============================================================================== //==============================================================================
@@ -1214,7 +1197,7 @@ public:
char nm[264] = { 0 }; char nm[264] = { 0 };
if (dispatch (effGetProgramNameIndexed, jlimit (0, getNumPrograms(), index), -1, nm, 0) != 0) if (dispatch (effGetProgramNameIndexed, jlimit (0, getNumPrograms(), index), -1, nm, 0) != 0)
return String (CharPointer_UTF8 (nm)).trim(); return String::fromUTF8 (nm).trim();
} }
} }
@@ -1285,7 +1268,8 @@ public:
handleUpdateNowIfNeeded(); handleUpdateNowIfNeeded();
for (int i = ComponentPeer::getNumPeers(); --i >= 0;) for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
ComponentPeer::getPeer(i)->performAnyPendingRepaintsNow(); if (ComponentPeer* p = ComponentPeer::getPeer(i))
p->performAnyPendingRepaintsNow();
} }
break; break;
@@ -1448,20 +1432,20 @@ public:
const fxSet* const set = (const fxSet*) data; const fxSet* const set = (const fxSet*) data;
if ((vst_swap (set->chunkMagic) != 'CcnK' && vst_swap (set->chunkMagic) != 'KncC') if ((set->chunkMagic != fxbName ("CcnK") && set->chunkMagic != fxbName ("KncC"))
|| vst_swap (set->version) > fxbVersionNum) || fxbSwap (set->version) > fxbVersionNum)
return false; return false;
if (vst_swap (set->fxMagic) == 'FxBk') if (set->fxMagic == fxbName ("FxBk"))
{ {
// bank of programs // bank of programs
if (vst_swap (set->numPrograms) >= 0) if (fxbSwap (set->numPrograms) >= 0)
{ {
const int oldProg = getCurrentProgram(); const int oldProg = getCurrentProgram();
const int numParams = vst_swap (((const fxProgram*) (set->programs))->numParams); const int numParams = fxbSwap (((const fxProgram*) (set->programs))->numParams);
const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float);
for (int i = 0; i < vst_swap (set->numPrograms); ++i) for (int i = 0; i < fxbSwap (set->numPrograms); ++i)
{ {
if (i != oldProg) if (i != oldProg)
{ {
@@ -1469,7 +1453,7 @@ public:
if (((const char*) prog) - ((const char*) set) >= (ssize_t) dataSize) if (((const char*) prog) - ((const char*) set) >= (ssize_t) dataSize)
return false; return false;
if (vst_swap (set->numPrograms) > 0) if (fxbSwap (set->numPrograms) > 0)
setCurrentProgram (i); setCurrentProgram (i);
if (! restoreProgramSettings (prog)) if (! restoreProgramSettings (prog))
@@ -1477,7 +1461,7 @@ public:
} }
} }
if (vst_swap (set->numPrograms) > 0) if (fxbSwap (set->numPrograms) > 0)
setCurrentProgram (oldProg); setCurrentProgram (oldProg);
const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + oldProg * progLen); const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + oldProg * progLen);
@@ -1488,38 +1472,38 @@ public:
return false; return false;
} }
} }
else if (vst_swap (set->fxMagic) == 'FxCk') else if (set->fxMagic == fxbName ("FxCk"))
{ {
// single program // single program
const fxProgram* const prog = (const fxProgram*) data; const fxProgram* const prog = (const fxProgram*) data;
if (vst_swap (prog->chunkMagic) != 'CcnK') if (prog->chunkMagic != fxbName ("CcnK"))
return false; return false;
changeProgramName (getCurrentProgram(), prog->prgName); changeProgramName (getCurrentProgram(), prog->prgName);
for (int i = 0; i < vst_swap (prog->numParams); ++i) for (int i = 0; i < fxbSwap (prog->numParams); ++i)
setParameter (i, vst_swapFloat (prog->params[i])); setParameter (i, fxbSwapFloat (prog->params[i]));
} }
else if (vst_swap (set->fxMagic) == 'FBCh' || vst_swap (set->fxMagic) == 'hCBF') else if (set->fxMagic == fxbName ("FBCh") || set->fxMagic == fxbName ("hCBF"))
{ {
// non-preset chunk // non-preset chunk
const fxChunkSet* const cset = (const fxChunkSet*) data; const fxChunkSet* const cset = (const fxChunkSet*) data;
if (vst_swap (cset->chunkSize) + sizeof (fxChunkSet) - 8 > (unsigned int) dataSize) if (fxbSwap (cset->chunkSize) + sizeof (fxChunkSet) - 8 > (unsigned int) dataSize)
return false; return false;
setChunkData (cset->chunk, vst_swap (cset->chunkSize), false); setChunkData (cset->chunk, fxbSwap (cset->chunkSize), false);
} }
else if (vst_swap (set->fxMagic) == 'FPCh' || vst_swap (set->fxMagic) == 'hCPF') else if (set->fxMagic == fxbName ("FPCh") || set->fxMagic == fxbName ("hCPF"))
{ {
// preset chunk // preset chunk
const fxProgramSet* const cset = (const fxProgramSet*) data; const fxProgramSet* const cset = (const fxProgramSet*) data;
if (vst_swap (cset->chunkSize) + sizeof (fxProgramSet) - 8 > (unsigned int) dataSize) if (fxbSwap (cset->chunkSize) + sizeof (fxProgramSet) - 8 > (unsigned int) dataSize)
return false; return false;
setChunkData (cset->chunk, vst_swap (cset->chunkSize), true); setChunkData (cset->chunk, fxbSwap (cset->chunkSize), true);
changeProgramName (getCurrentProgram(), cset->name); changeProgramName (getCurrentProgram(), cset->name);
} }
@@ -1547,14 +1531,14 @@ public:
dest.setSize (totalLen, true); dest.setSize (totalLen, true);
fxChunkSet* const set = (fxChunkSet*) dest.getData(); fxChunkSet* const set = (fxChunkSet*) dest.getData();
set->chunkMagic = vst_swap ('CcnK'); set->chunkMagic = fxbName ("CcnK");
set->byteSize = 0; set->byteSize = 0;
set->fxMagic = vst_swap ('FBCh'); set->fxMagic = fxbName ("FBCh");
set->version = vst_swap (fxbVersionNum); set->version = fxbSwap (fxbVersionNum);
set->fxID = vst_swap (getUID()); set->fxID = fxbSwap (getUID());
set->fxVersion = vst_swap (getVersionNumber()); set->fxVersion = fxbSwap (getVersionNumber());
set->numPrograms = vst_swap (numPrograms); set->numPrograms = fxbSwap (numPrograms);
set->chunkSize = vst_swap ((VstInt32) chunk.getSize()); set->chunkSize = fxbSwap ((VstInt32) chunk.getSize());
chunk.copyTo (set->chunk, 0, chunk.getSize()); chunk.copyTo (set->chunk, 0, chunk.getSize());
} }
@@ -1564,14 +1548,14 @@ public:
dest.setSize (totalLen, true); dest.setSize (totalLen, true);
fxProgramSet* const set = (fxProgramSet*) dest.getData(); fxProgramSet* const set = (fxProgramSet*) dest.getData();
set->chunkMagic = vst_swap ('CcnK'); set->chunkMagic = fxbName ("CcnK");
set->byteSize = 0; set->byteSize = 0;
set->fxMagic = vst_swap ('FPCh'); set->fxMagic = fxbName ("FPCh");
set->version = vst_swap (fxbVersionNum); set->version = fxbSwap (fxbVersionNum);
set->fxID = vst_swap (getUID()); set->fxID = fxbSwap (getUID());
set->fxVersion = vst_swap (getVersionNumber()); set->fxVersion = fxbSwap (getVersionNumber());
set->numPrograms = vst_swap (numPrograms); set->numPrograms = fxbSwap (numPrograms);
set->chunkSize = vst_swap ((VstInt32) chunk.getSize()); set->chunkSize = fxbSwap ((VstInt32) chunk.getSize());
getCurrentProgramName().copyToUTF8 (set->name, sizeof (set->name) - 1); getCurrentProgramName().copyToUTF8 (set->name, sizeof (set->name) - 1);
chunk.copyTo (set->chunk, 0, chunk.getSize()); chunk.copyTo (set->chunk, 0, chunk.getSize());
@@ -1586,13 +1570,13 @@ public:
dest.setSize (len, true); dest.setSize (len, true);
fxSet* const set = (fxSet*) dest.getData(); fxSet* const set = (fxSet*) dest.getData();
set->chunkMagic = vst_swap ('CcnK'); set->chunkMagic = fxbName ("CcnK");
set->byteSize = 0; set->byteSize = 0;
set->fxMagic = vst_swap ('FxBk'); set->fxMagic = fxbName ("FxBk");
set->version = vst_swap (fxbVersionNum); set->version = fxbSwap (fxbVersionNum);
set->fxID = vst_swap (getUID()); set->fxID = fxbSwap (getUID());
set->fxVersion = vst_swap (getVersionNumber()); set->fxVersion = fxbSwap (getVersionNumber());
set->numPrograms = vst_swap (numPrograms); set->numPrograms = fxbSwap (numPrograms);
const int oldProgram = getCurrentProgram(); const int oldProgram = getCurrentProgram();
MemoryBlock oldSettings; MemoryBlock oldSettings;
@@ -1687,12 +1671,12 @@ private:
bool restoreProgramSettings (const fxProgram* const prog) bool restoreProgramSettings (const fxProgram* const prog)
{ {
if (vst_swap (prog->chunkMagic) == 'CcnK' && vst_swap (prog->fxMagic) == 'FxCk') if (prog->chunkMagic == fxbName ("CcnK") && prog->fxMagic == fxbName ("FxCk"))
{ {
changeProgramName (getCurrentProgram(), prog->prgName); changeProgramName (getCurrentProgram(), prog->prgName);
for (int i = 0; i < vst_swap (prog->numParams); ++i) for (int i = 0; i < fxbSwap (prog->numParams); ++i)
setParameter (i, vst_swapFloat (prog->params[i])); setParameter (i, fxbSwapFloat (prog->params[i]));
return true; return true;
} }
@@ -1741,18 +1725,18 @@ private:
{ {
const int numParams = getNumParameters(); const int numParams = getNumParameters();
prog->chunkMagic = vst_swap ('CcnK'); prog->chunkMagic = fxbName ("CcnK");
prog->byteSize = 0; prog->byteSize = 0;
prog->fxMagic = vst_swap ('FxCk'); prog->fxMagic = fxbName ("FxCk");
prog->version = vst_swap (fxbVersionNum); prog->version = fxbSwap (fxbVersionNum);
prog->fxID = vst_swap (getUID()); prog->fxID = fxbSwap (getUID());
prog->fxVersion = vst_swap (getVersionNumber()); prog->fxVersion = fxbSwap (getVersionNumber());
prog->numParams = vst_swap (numParams); prog->numParams = fxbSwap (numParams);
getCurrentProgramName().copyToUTF8 (prog->prgName, sizeof (prog->prgName) - 1); getCurrentProgramName().copyToUTF8 (prog->prgName, sizeof (prog->prgName) - 1);
for (int i = 0; i < numParams; ++i) for (int i = 0; i < numParams; ++i)
prog->params[i] = vst_swapFloat (getParameter (i)); prog->params[i] = fxbSwapFloat (getParameter (i));
} }
void updateStoredProgramNames() void updateStoredProgramNames()
@@ -1894,7 +1878,8 @@ private:
}; };
//============================================================================== //==============================================================================
static Array <VSTPluginWindow*> activeVSTWindows; class VSTPluginWindow;
static Array<VSTPluginWindow*> activeVSTWindows;
//============================================================================== //==============================================================================
class VSTPluginWindow : public AudioProcessorEditor, class VSTPluginWindow : public AudioProcessorEditor,
@@ -2232,10 +2217,11 @@ private:
#pragma warning (push) #pragma warning (push)
#pragma warning (disable: 4244) #pragma warning (disable: 4244)
originalWndProc = (void*) GetWindowLongPtr (pluginHWND, GWLP_WNDPROC);
if (! pluginWantsKeys) if (! pluginWantsKeys)
{
originalWndProc = (void*) GetWindowLongPtr (pluginHWND, GWLP_WNDPROC);
SetWindowLongPtr (pluginHWND, GWLP_WNDPROC, (LONG_PTR) vstHookWndProc); SetWindowLongPtr (pluginHWND, GWLP_WNDPROC, (LONG_PTR) vstHookWndProc);
}
#pragma warning (pop) #pragma warning (pop)
@@ -2317,26 +2303,24 @@ private:
{ {
if (isOpen) if (isOpen)
{ {
#if ! (JUCE_MAC && JUCE_SUPPORT_CARBON)
JUCE_VST_LOG ("Closing VST UI: " + plugin.getName()); JUCE_VST_LOG ("Closing VST UI: " + plugin.getName());
isOpen = false; isOpen = false;
dispatch (effEditClose, 0, 0, 0, 0); dispatch (effEditClose, 0, 0, 0, 0);
stopTimer(); stopTimer();
#if JUCE_WINDOWS #if JUCE_WINDOWS
#pragma warning (push) #pragma warning (push)
#pragma warning (disable: 4244) #pragma warning (disable: 4244)
if (pluginHWND != 0 && IsWindow (pluginHWND)) if (originalWndProc != 0 && pluginHWND != 0 && IsWindow (pluginHWND))
SetWindowLongPtr (pluginHWND, GWLP_WNDPROC, (LONG_PTR) originalWndProc); SetWindowLongPtr (pluginHWND, GWLP_WNDPROC, (LONG_PTR) originalWndProc);
#pragma warning (pop) #pragma warning (pop)
originalWndProc = 0;
pluginHWND = 0; pluginHWND = 0;
#elif JUCE_LINUX #elif JUCE_LINUX
pluginWindow = 0; pluginWindow = 0;
pluginProc = 0; pluginProc = 0;
#endif #endif
#endif
} }
} }
@@ -2845,7 +2829,7 @@ FileSearchPath VSTPluginFormat::getDefaultLocationsToSearch()
const XmlElement* VSTPluginFormat::getVSTXML (AudioPluginInstance* plugin) const XmlElement* VSTPluginFormat::getVSTXML (AudioPluginInstance* plugin)
{ {
if (VSTPluginInstance* const vst = dynamic_cast <VSTPluginInstance*> (plugin)) if (VSTPluginInstance* const vst = dynamic_cast<VSTPluginInstance*> (plugin))
if (vst->module != nullptr) if (vst->module != nullptr)
return vst->module->vstXml.get(); return vst->module->vstXml.get();
@@ -2854,7 +2838,7 @@ const XmlElement* VSTPluginFormat::getVSTXML (AudioPluginInstance* plugin)
bool VSTPluginFormat::loadFromFXBFile (AudioPluginInstance* plugin, const void* data, size_t dataSize) bool VSTPluginFormat::loadFromFXBFile (AudioPluginInstance* plugin, const void* data, size_t dataSize)
{ {
if (VSTPluginInstance* vst = dynamic_cast <VSTPluginInstance*> (plugin)) if (VSTPluginInstance* vst = dynamic_cast<VSTPluginInstance*> (plugin))
return vst->loadFromFXBFile (data, dataSize); return vst->loadFromFXBFile (data, dataSize);
return false; return false;
@@ -2862,7 +2846,7 @@ bool VSTPluginFormat::loadFromFXBFile (AudioPluginInstance* plugin, const void*
bool VSTPluginFormat::saveToFXBFile (AudioPluginInstance* plugin, MemoryBlock& dest, bool asFXB) bool VSTPluginFormat::saveToFXBFile (AudioPluginInstance* plugin, MemoryBlock& dest, bool asFXB)
{ {
if (VSTPluginInstance* vst = dynamic_cast <VSTPluginInstance*> (plugin)) if (VSTPluginInstance* vst = dynamic_cast<VSTPluginInstance*> (plugin))
return vst->saveToFXBFile (dest, asFXB); return vst->saveToFXBFile (dest, asFXB);
return false; return false;
@@ -2870,7 +2854,7 @@ bool VSTPluginFormat::saveToFXBFile (AudioPluginInstance* plugin, MemoryBlock& d
bool VSTPluginFormat::getChunkData (AudioPluginInstance* plugin, MemoryBlock& result, bool isPreset) bool VSTPluginFormat::getChunkData (AudioPluginInstance* plugin, MemoryBlock& result, bool isPreset)
{ {
if (VSTPluginInstance* vst = dynamic_cast <VSTPluginInstance*> (plugin)) if (VSTPluginInstance* vst = dynamic_cast<VSTPluginInstance*> (plugin))
return vst->getChunkData (result, isPreset, 128); return vst->getChunkData (result, isPreset, 128);
return false; return false;
@@ -2878,7 +2862,7 @@ bool VSTPluginFormat::getChunkData (AudioPluginInstance* plugin, MemoryBlock& re
bool VSTPluginFormat::setChunkData (AudioPluginInstance* plugin, const void* data, int size, bool isPreset) bool VSTPluginFormat::setChunkData (AudioPluginInstance* plugin, const void* data, int size, bool isPreset)
{ {
if (VSTPluginInstance* vst = dynamic_cast <VSTPluginInstance*> (plugin)) if (VSTPluginInstance* vst = dynamic_cast<VSTPluginInstance*> (plugin))
return vst->setChunkData (data, size, isPreset); return vst->setChunkData (data, size, isPreset);
return false; return false;
@@ -2888,13 +2872,13 @@ void VSTPluginFormat::setExtraFunctions (AudioPluginInstance* plugin, ExtraFunct
{ {
ScopedPointer<ExtraFunctions> f (functions); ScopedPointer<ExtraFunctions> f (functions);
if (VSTPluginInstance* vst = dynamic_cast <VSTPluginInstance*> (plugin)) if (VSTPluginInstance* vst = dynamic_cast<VSTPluginInstance*> (plugin))
vst->extraFunctions = f; vst->extraFunctions = f;
} }
VSTPluginFormat::VstIntPtr JUCE_CALLTYPE VSTPluginFormat::dispatcher (AudioPluginInstance* plugin, int32 opcode, int32 index, VstIntPtr value, void* ptr, float opt) VSTPluginFormat::VstIntPtr JUCE_CALLTYPE VSTPluginFormat::dispatcher (AudioPluginInstance* plugin, int32 opcode, int32 index, VstIntPtr value, void* ptr, float opt)
{ {
if (VSTPluginInstance* vst = dynamic_cast <VSTPluginInstance*> (plugin)) if (VSTPluginInstance* vst = dynamic_cast<VSTPluginInstance*> (plugin))
return vst->dispatch (opcode, index, value, ptr, opt); return vst->dispatch (opcode, index, value, ptr, opt);
return 0; return 0;
@@ -41,8 +41,9 @@
//============================================================================== //==============================================================================
#if JUCE_MAC #if JUCE_MAC
#if (JUCE_PLUGINHOST_VST || JUCE_PLUGINHOST_AU) \ #if JUCE_SUPPORT_CARBON \
|| ! (defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6) && ((JUCE_PLUGINHOST_VST || JUCE_PLUGINHOST_AU) \
|| ! (defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6))
#define Point CarbonDummyPointName // (workaround to avoid definition of "Point" by old Carbon headers) #define Point CarbonDummyPointName // (workaround to avoid definition of "Point" by old Carbon headers)
#define Component CarbonDummyCompName #define Component CarbonDummyCompName
#include <Carbon/Carbon.h> #include <Carbon/Carbon.h>
@@ -112,27 +113,24 @@ struct AutoResizingNSViewComponentWithParent : public AutoResizingNSViewCompone
setView (v); setView (v);
[v release]; [v release];
startTimer (100); startTimer (30);
}
NSView* getChildView() const
{
if (NSView* parent = (NSView*) getView())
if ([[parent subviews] count] > 0)
return [[parent subviews] objectAtIndex: 0];
return nil;
} }
void timerCallback() override void timerCallback() override
{ {
if (NSView* parent = (NSView*) getView()) if (NSView* child = getChildView())
{ {
if ([[parent subviews] count] > 0) stopTimer();
{ setView (child);
if (NSView* child = [[parent subviews] objectAtIndex: 0])
{
NSRect f = [parent frame];
NSSize newSize = [child frame].size;
if (f.size.width != newSize.width || f.size.height != newSize.height)
{
f.size = newSize;
[parent setFrame: f];
}
}
}
} }
} }
}; };
@@ -76,6 +76,7 @@ class AudioProcessor;
#include "processors/juce_AudioPlayHead.h" #include "processors/juce_AudioPlayHead.h"
#include "processors/juce_AudioProcessorEditor.h" #include "processors/juce_AudioProcessorEditor.h"
#include "processors/juce_AudioProcessorListener.h" #include "processors/juce_AudioProcessorListener.h"
#include "processors/juce_AudioProcessorParameter.h"
#include "processors/juce_AudioProcessor.h" #include "processors/juce_AudioProcessor.h"
#include "processors/juce_PluginDescription.h" #include "processors/juce_PluginDescription.h"
#include "processors/juce_AudioPluginInstance.h" #include "processors/juce_AudioPluginInstance.h"
@@ -1,7 +1,7 @@
{ {
"id": "juce_audio_processors", "id": "juce_audio_processors",
"name": "JUCE audio plugin hosting classes", "name": "JUCE audio plugin hosting classes",
"version": "3.0.5", "version": "3.0.8",
"description": "Classes for loading and playing VST, AU, or internally-generated audio processors.", "description": "Classes for loading and playing VST, AU, or internally-generated audio processors.",
"website": "http://www.juce.com/juce", "website": "http://www.juce.com/juce",
"license": "GPL/Commercial", "license": "GPL/Commercial",
@@ -118,19 +118,6 @@ void AudioProcessor::setParameterNotifyingHost (const int parameterIndex,
sendParamChangeMessageToListeners (parameterIndex, newValue); sendParamChangeMessageToListeners (parameterIndex, newValue);
} }
String AudioProcessor::getParameterName (int parameterIndex, int maximumStringLength)
{
return getParameterName (parameterIndex).substring (0, maximumStringLength);
}
String AudioProcessor::getParameterText (int parameterIndex, int maximumStringLength)
{
return getParameterText (parameterIndex).substring (0, maximumStringLength);
}
int AudioProcessor::getParameterNumSteps (int /*parameterIndex*/) { return 0x7fffffff; }
float AudioProcessor::getParameterDefaultValue (int /*parameterIndex*/) { return 0.0f; }
AudioProcessorListener* AudioProcessor::getListenerLocked (const int index) const noexcept AudioProcessorListener* AudioProcessor::getListenerLocked (const int index) const noexcept
{ {
const ScopedLock sl (listenerLock); const ScopedLock sl (listenerLock);
@@ -201,9 +188,129 @@ void AudioProcessor::updateHostDisplay()
l->audioProcessorChanged (this); l->audioProcessorChanged (this);
} }
String AudioProcessor::getParameterLabel (int) const { return String(); } const OwnedArray<AudioProcessorParameter>& AudioProcessor::getParameters() const noexcept
bool AudioProcessor::isParameterAutomatable (int) const { return true; } {
bool AudioProcessor::isMetaParameter (int) const { return false; } return managedParameters;
}
int AudioProcessor::getNumParameters()
{
return managedParameters.size();
}
float AudioProcessor::getParameter (int index)
{
if (AudioProcessorParameter* p = getParamChecked (index))
return p->getValue();
return 0;
}
void AudioProcessor::setParameter (int index, float newValue)
{
if (AudioProcessorParameter* p = getParamChecked (index))
p->setValue (newValue);
}
float AudioProcessor::getParameterDefaultValue (int index)
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->getDefaultValue();
return 0;
}
const String AudioProcessor::getParameterName (int index)
{
if (AudioProcessorParameter* p = getParamChecked (index))
return p->getName (512);
return String();
}
String AudioProcessor::getParameterName (int index, int maximumStringLength)
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->getName (maximumStringLength);
return getParameterName (index).substring (0, maximumStringLength);
}
const String AudioProcessor::getParameterText (int index)
{
return getParameterText (index, 1024);
}
String AudioProcessor::getParameterText (int index, int maximumStringLength)
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->getText (p->getValue(), maximumStringLength);
return getParameterText (index).substring (0, maximumStringLength);
}
int AudioProcessor::getParameterNumSteps (int index)
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->getNumSteps();
return AudioProcessor::getDefaultNumParameterSteps();
}
int AudioProcessor::getDefaultNumParameterSteps() noexcept
{
return 0x7fffffff;
}
String AudioProcessor::getParameterLabel (int index) const
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->getLabel();
return String();
}
bool AudioProcessor::isParameterAutomatable (int index) const
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->isAutomatable();
return true;
}
bool AudioProcessor::isParameterOrientationInverted (int index) const
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->isOrientationInverted();
return false;
}
bool AudioProcessor::isMetaParameter (int index) const
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->isMetaParameter();
return false;
}
AudioProcessorParameter* AudioProcessor::getParamChecked (int index) const noexcept
{
AudioProcessorParameter* p = managedParameters[index];
// If you hit this, then you're either trying to access parameters that are out-of-range,
// or you're not using addParameter and the managed parameter list, but have failed
// to override some essential virtual methods and implement them appropriately.
jassert (p != nullptr);
return p;
}
void AudioProcessor::addParameter (AudioProcessorParameter* p)
{
p->processor = this;
p->parameterIndex = managedParameters.size();
managedParameters.add (p);
}
void AudioProcessor::suspendProcessing (const bool shouldBeSuspended) void AudioProcessor::suspendProcessing (const bool shouldBeSuspended)
{ {
@@ -230,9 +337,6 @@ AudioProcessorEditor* AudioProcessor::createEditorIfNeeded()
AudioProcessorEditor* const ed = createEditor(); AudioProcessorEditor* const ed = createEditor();
// You must make your hasEditor() method return a consistent result!
jassert (hasEditor() == (ed != nullptr));
if (ed != nullptr) if (ed != nullptr)
{ {
// you must give your editor comp a size before returning it.. // you must give your editor comp a size before returning it..
@@ -242,6 +346,9 @@ AudioProcessorEditor* AudioProcessor::createEditorIfNeeded()
activeEditor = ed; activeEditor = ed;
} }
// You must make your hasEditor() method return a consistent result!
jassert (hasEditor() == (ed != nullptr));
return ed; return ed;
} }
@@ -294,6 +401,47 @@ XmlElement* AudioProcessor::getXmlFromBinary (const void* data, const int sizeIn
void AudioProcessorListener::audioProcessorParameterChangeGestureBegin (AudioProcessor*, int) {} void AudioProcessorListener::audioProcessorParameterChangeGestureBegin (AudioProcessor*, int) {}
void AudioProcessorListener::audioProcessorParameterChangeGestureEnd (AudioProcessor*, int) {} void AudioProcessorListener::audioProcessorParameterChangeGestureEnd (AudioProcessor*, int) {}
//==============================================================================
AudioProcessorParameter::AudioProcessorParameter() noexcept
: processor (nullptr), parameterIndex (-1)
{}
AudioProcessorParameter::~AudioProcessorParameter() {}
void AudioProcessorParameter::setValueNotifyingHost (float newValue)
{
// This method can't be used until the parameter has been attached to a processor!
jassert (processor != nullptr && parameterIndex >= 0);
return processor->setParameterNotifyingHost (parameterIndex, newValue);
}
void AudioProcessorParameter::beginChangeGesture()
{
// This method can't be used until the parameter has been attached to a processor!
jassert (processor != nullptr && parameterIndex >= 0);
processor->beginParameterChangeGesture (parameterIndex);
}
void AudioProcessorParameter::endChangeGesture()
{
// This method can't be used until the parameter has been attached to a processor!
jassert (processor != nullptr && parameterIndex >= 0);
processor->endParameterChangeGesture (parameterIndex);
}
bool AudioProcessorParameter::isOrientationInverted() const { return false; }
bool AudioProcessorParameter::isAutomatable() const { return true; }
bool AudioProcessorParameter::isMetaParameter() const { return false; }
int AudioProcessorParameter::getNumSteps() const { return AudioProcessor::getDefaultNumParameterSteps(); }
String AudioProcessorParameter::getText (float value, int /*maximumStringLength*/) const
{
return String (value, 2);
}
//============================================================================== //==============================================================================
bool AudioPlayHead::CurrentPositionInfo::operator== (const CurrentPositionInfo& other) const noexcept bool AudioPlayHead::CurrentPositionInfo::operator== (const CurrentPositionInfo& other) const noexcept
{ {
@@ -44,11 +44,7 @@ class JUCE_API AudioProcessor
{ {
protected: protected:
//============================================================================== //==============================================================================
/** Constructor. /** Constructor. */
You can also do your initialisation tasks in the initialiseFilterInfo()
call, which will be made after this object has been created.
*/
AudioProcessor(); AudioProcessor();
public: public:
@@ -293,9 +289,9 @@ public:
filter will return an empty buffer, but won't block the audio thread like it would filter will return an empty buffer, but won't block the audio thread like it would
do if you use the getCallbackLock() critical section to synchronise access. do if you use the getCallbackLock() critical section to synchronise access.
If you're going to use this, your processBlock() method must call isSuspended() and Any code that calls processBlock() should call isSuspended() before doing so, and
check whether it's suspended or not. If it is, then it should skip doing any real if the processor is suspended, it should avoid the call and emit silence or
processing, either emitting silence or passing the input through unchanged. whatever is appropriate.
@see getCallbackLock @see getCallbackLock
*/ */
@@ -329,7 +325,7 @@ public:
/** Called by the host to tell this processor whether it's being used in a non-realtime /** Called by the host to tell this processor whether it's being used in a non-realtime
capacity for offline rendering or bouncing. capacity for offline rendering or bouncing.
*/ */
void setNonRealtime (bool isNonRealtime) noexcept; virtual void setNonRealtime (bool isNonRealtime) noexcept;
//============================================================================== //==============================================================================
/** Creates the filter's UI. /** Creates the filter's UI.
@@ -381,10 +377,10 @@ public:
/** This must return the correct value immediately after the object has been /** This must return the correct value immediately after the object has been
created, and mustn't change the number of parameters later. created, and mustn't change the number of parameters later.
*/ */
virtual int getNumParameters() = 0; virtual int getNumParameters();
/** Returns the name of a particular parameter. */ /** Returns the name of a particular parameter. */
virtual const String getParameterName (int parameterIndex) = 0; virtual const String getParameterName (int parameterIndex);
/** Called by the host to find out the value of one of the filter's parameters. /** Called by the host to find out the value of one of the filter's parameters.
@@ -394,10 +390,10 @@ public:
It's also likely to be called by non-UI threads, so the code in here should It's also likely to be called by non-UI threads, so the code in here should
be thread-aware. be thread-aware.
*/ */
virtual float getParameter (int parameterIndex) = 0; virtual float getParameter (int parameterIndex);
/** Returns the value of a parameter as a text string. */ /** Returns the value of a parameter as a text string. */
virtual const String getParameterText (int parameterIndex) = 0; virtual const String getParameterText (int parameterIndex);
/** Returns the name of a parameter as a text string with a preferred maximum length. /** Returns the name of a parameter as a text string with a preferred maximum length.
If you want to provide customised short versions of your parameter names that If you want to provide customised short versions of your parameter names that
@@ -418,12 +414,18 @@ public:
virtual String getParameterText (int parameterIndex, int maximumStringLength); virtual String getParameterText (int parameterIndex, int maximumStringLength);
/** Returns the number of discrete steps that this parameter can represent. /** Returns the number of discrete steps that this parameter can represent.
The default return value if you don't implement this method is 0x7fffffff. The default return value if you don't implement this method is
AudioProcessor::getDefaultNumParameterSteps().
If your parameter is boolean, then you may want to make this return 2. If your parameter is boolean, then you may want to make this return 2.
The value that is returned may or may not be used, depending on the host. The value that is returned may or may not be used, depending on the host.
*/ */
virtual int getParameterNumSteps (int parameterIndex); virtual int getParameterNumSteps (int parameterIndex);
/** Returns the default number of steps for a parameter.
@see getParameterNumSteps
*/
static int getDefaultNumParameterSteps() noexcept;
/** Returns the default value for the parameter. /** Returns the default value for the parameter.
By default, this just returns 0. By default, this just returns 0.
The value that is returned may or may not be used, depending on the host. The value that is returned may or may not be used, depending on the host.
@@ -435,6 +437,11 @@ public:
*/ */
virtual String getParameterLabel (int index) const; virtual String getParameterLabel (int index) const;
/** This can be overridden to tell the host that particular parameters operate in the
reverse direction. (Not all plugin formats or hosts will actually use this information).
*/
virtual bool isParameterOrientationInverted (int index) const;
/** The host will call this method to change the value of one of the filter's parameters. /** The host will call this method to change the value of one of the filter's parameters.
The host may call this at any time, including during the audio processing The host may call this at any time, including during the audio processing
@@ -448,7 +455,7 @@ public:
The value passed will be between 0 and 1.0. The value passed will be between 0 and 1.0.
*/ */
virtual void setParameter (int parameterIndex, float newValue) = 0; virtual void setParameter (int parameterIndex, float newValue);
/** Your filter can call this when it needs to change one of its parameters. /** Your filter can call this when it needs to change one of its parameters.
@@ -463,16 +470,13 @@ public:
void setParameterNotifyingHost (int parameterIndex, float newValue); void setParameterNotifyingHost (int parameterIndex, float newValue);
/** Returns true if the host can automate this parameter. /** Returns true if the host can automate this parameter.
By default, this returns true for all parameters. By default, this returns true for all parameters.
*/ */
virtual bool isParameterAutomatable (int parameterIndex) const; virtual bool isParameterAutomatable (int parameterIndex) const;
/** Should return true if this parameter is a "meta" parameter. /** Should return true if this parameter is a "meta" parameter.
A meta-parameter is a parameter that changes other params. It is used A meta-parameter is a parameter that changes other params. It is used
by some hosts (e.g. AudioUnit hosts). by some hosts (e.g. AudioUnit hosts).
By default this returns false. By default this returns false.
*/ */
virtual bool isMetaParameter (int parameterIndex) const; virtual bool isMetaParameter (int parameterIndex) const;
@@ -503,6 +507,16 @@ public:
*/ */
void updateHostDisplay(); void updateHostDisplay();
//==============================================================================
/** Adds a parameter to the list.
The parameter object will be managed and deleted automatically by the list
when no longer needed.
*/
void addParameter (AudioProcessorParameter*);
/** Returns the current list of parameters. */
const OwnedArray<AudioProcessorParameter>& getParameters() const noexcept;
//============================================================================== //==============================================================================
/** Returns the number of preset programs the filter supports. /** Returns the number of preset programs the filter supports.
@@ -659,6 +673,9 @@ private:
CriticalSection callbackLock, listenerLock; CriticalSection callbackLock, listenerLock;
String inputSpeakerArrangement, outputSpeakerArrangement; String inputSpeakerArrangement, outputSpeakerArrangement;
OwnedArray<AudioProcessorParameter> managedParameters;
AudioProcessorParameter* getParamChecked (int) const noexcept;
#if JUCE_DEBUG #if JUCE_DEBUG
BigInteger changingParams; BigInteger changingParams;
#endif #endif
@@ -22,16 +22,22 @@
============================================================================== ==============================================================================
*/ */
AudioProcessorEditor::AudioProcessorEditor (AudioProcessor* const p) AudioProcessorEditor::AudioProcessorEditor (AudioProcessor& p) noexcept : processor (p)
: owner (p) {
}
AudioProcessorEditor::AudioProcessorEditor (AudioProcessor* p) noexcept : processor (*p)
{ {
// the filter must be valid.. // the filter must be valid..
jassert (owner != nullptr); jassert (p != nullptr);
} }
AudioProcessorEditor::~AudioProcessorEditor() AudioProcessorEditor::~AudioProcessorEditor()
{ {
// if this fails, then the wrapper hasn't called editorBeingDeleted() on the // if this fails, then the wrapper hasn't called editorBeingDeleted() on the
// filter for some reason.. // filter for some reason..
jassert (owner->getActiveEditor() != this); jassert (processor.getActiveEditor() != this);
} }
void AudioProcessorEditor::setControlHighlight (ParameterControlHighlightInfo) {}
int AudioProcessorEditor::getControlParameterIndex (Component&) { return -1; }
@@ -39,9 +39,11 @@ class JUCE_API AudioProcessorEditor : public Component
{ {
protected: protected:
//============================================================================== //==============================================================================
/** Creates an editor for the specified processor. /** Creates an editor for the specified processor. */
*/ AudioProcessorEditor (AudioProcessor&) noexcept;
AudioProcessorEditor (AudioProcessor* owner);
/** Creates an editor for the specified processor. */
AudioProcessorEditor (AudioProcessor*) noexcept;
public: public:
/** Destructor. */ /** Destructor. */
@@ -49,14 +51,40 @@ public:
//============================================================================== //==============================================================================
/** Returns a pointer to the processor that this editor represents. */ /** The AudioProcessor that this editor represents. */
AudioProcessor* getAudioProcessor() const noexcept { return owner; } AudioProcessor& processor;
/** Returns a pointer to the processor that this editor represents.
This method is here to support legacy code, but it's easier to just use the
AudioProcessorEditor::processor member variable directly to get this object.
*/
AudioProcessor* getAudioProcessor() const noexcept { return &processor; }
//==============================================================================
/** Used by the setParameterHighlighting() method. */
struct ParameterControlHighlightInfo
{
int parameterIndex;
bool isHighlighted;
Colour suggestedColour;
};
/** Some types of plugin can call this to suggest that the control for a particular
parameter should be highlighted.
Currently only AAX plugins will call this, and implementing it is optional.
*/
virtual void setControlHighlight (ParameterControlHighlightInfo);
/** Called by certain plug-in wrappers to find out whether a component is used
to control a parameter.
If the given component represents a particular plugin parameter, then this
method should return the index of that parameter. If not, it should return -1.
Currently only AAX plugins will call this, and implementing it is optional.
*/
virtual int getControlParameterIndex (Component&);
private: private:
//==============================================================================
AudioProcessor* const owner;
JUCE_DECLARE_NON_COPYABLE (AudioProcessorEditor) JUCE_DECLARE_NON_COPYABLE (AudioProcessorEditor)
}; };
@@ -388,11 +388,11 @@ public:
private: private:
//============================================================================== //==============================================================================
ReferenceCountedArray <Node> nodes; ReferenceCountedArray<Node> nodes;
OwnedArray <Connection> connections; OwnedArray<Connection> connections;
uint32 lastNodeId; uint32 lastNodeId;
AudioSampleBuffer renderingBuffers; AudioSampleBuffer renderingBuffers;
OwnedArray <MidiBuffer> midiBuffers; OwnedArray<MidiBuffer> midiBuffers;
Array<void*> renderingOps; Array<void*> renderingOps;
friend class AudioGraphIOProcessor; friend class AudioGraphIOProcessor;
@@ -27,12 +27,12 @@ class ProcessorParameterPropertyComp : public PropertyComponent,
private Timer private Timer
{ {
public: public:
ProcessorParameterPropertyComp (const String& name, AudioProcessor& p, const int index_) ProcessorParameterPropertyComp (const String& name, AudioProcessor& p, int paramIndex)
: PropertyComponent (name), : PropertyComponent (name),
owner (p), owner (p),
index (index_), index (paramIndex),
paramHasChanged (false), paramHasChanged (false),
slider (p, index_) slider (p, paramIndex)
{ {
startTimer (100); startTimer (100);
addAndMakeVisible (slider); addAndMakeVisible (slider);
@@ -44,15 +44,19 @@ public:
owner.removeListener (this); owner.removeListener (this);
} }
void refresh() void refresh() override
{ {
paramHasChanged = false; paramHasChanged = false;
slider.setValue (owner.getParameter (index), dontSendNotification);
if (slider.getThumbBeingDragged() < 0)
slider.setValue (owner.getParameter (index), dontSendNotification);
slider.updateText();
} }
void audioProcessorChanged (AudioProcessor*) {} void audioProcessorChanged (AudioProcessor*) override {}
void audioProcessorParameterChanged (AudioProcessor*, int parameterIndex, float) void audioProcessorParameterChanged (AudioProcessor*, int parameterIndex, float) override
{ {
if (parameterIndex == index) if (parameterIndex == index)
paramHasChanged = true; paramHasChanged = true;
@@ -76,27 +80,34 @@ private:
class ParamSlider : public Slider class ParamSlider : public Slider
{ {
public: public:
ParamSlider (AudioProcessor& p, const int index_) ParamSlider (AudioProcessor& p, int paramIndex) : owner (p), index (paramIndex)
: owner (p),
index (index_)
{ {
setRange (0.0, 1.0, 0.0); const int steps = owner.getParameterNumSteps (index);
if (steps > 1 && steps < 0x7fffffff)
setRange (0.0, 1.0, 1.0 / (steps - 1.0));
else
setRange (0.0, 1.0);
setSliderStyle (Slider::LinearBar); setSliderStyle (Slider::LinearBar);
setTextBoxIsEditable (false); setTextBoxIsEditable (false);
setScrollWheelEnabled (false); setScrollWheelEnabled (true);
} }
void valueChanged() void valueChanged() override
{ {
const float newVal = (float) getValue(); const float newVal = (float) getValue();
if (owner.getParameter (index) != newVal) if (owner.getParameter (index) != newVal)
{
owner.setParameterNotifyingHost (index, newVal); owner.setParameterNotifyingHost (index, newVal);
updateText();
}
} }
String getTextFromValue (double /*value*/) String getTextFromValue (double /*value*/) override
{ {
return owner.getParameterText (index); return owner.getParameterText (index) + " " + owner.getParameterLabel (index).trimEnd();
} }
private: private:
@@ -71,18 +71,26 @@ PluginDescription& PluginDescription::operator= (const PluginDescription& other)
return *this; return *this;
} }
bool PluginDescription::isDuplicateOf (const PluginDescription& other) const bool PluginDescription::isDuplicateOf (const PluginDescription& other) const noexcept
{ {
return fileOrIdentifier == other.fileOrIdentifier return fileOrIdentifier == other.fileOrIdentifier
&& uid == other.uid; && uid == other.uid;
} }
static String getPluginDescSuffix (const PluginDescription& d)
{
return "-" + String::toHexString (d.fileOrIdentifier.hashCode())
+ "-" + String::toHexString (d.uid);
}
bool PluginDescription::matchesIdentifierString (const String& identifierString) const
{
return identifierString.endsWithIgnoreCase (getPluginDescSuffix (*this));
}
String PluginDescription::createIdentifierString() const String PluginDescription::createIdentifierString() const
{ {
return pluginFormatName return pluginFormatName + "-" + name + getPluginDescSuffix (*this);
+ "-" + name
+ "-" + String::toHexString (fileOrIdentifier.hashCode())
+ "-" + String::toHexString (uid);
} }
XmlElement* PluginDescription::createXml() const XmlElement* PluginDescription::createXml() const
@@ -107,7 +107,15 @@ public:
This isn't quite as simple as them just having the same file (because of This isn't quite as simple as them just having the same file (because of
shell plug-ins). shell plug-ins).
*/ */
bool isDuplicateOf (const PluginDescription& other) const; bool isDuplicateOf (const PluginDescription& other) const noexcept;
/** Return true if this description is equivalent to another one which created the
given identifier string.
Note that this isn't quite as simple as them just calling createIdentifierString()
and comparing the strings, because the identifers can differ (thanks to shell plug-ins).
*/
bool matchesIdentifierString (const String& identifierString) const;
//============================================================================== //==============================================================================
/** Returns a string that can be saved and used to uniquely identify the /** Returns a string that can be saved and used to uniquely identify the
@@ -46,7 +46,7 @@ PluginDescription* KnownPluginList::getTypeForFile (const String& fileOrIdentifi
PluginDescription* KnownPluginList::getTypeForIdentifierString (const String& identifierString) const PluginDescription* KnownPluginList::getTypeForIdentifierString (const String& identifierString) const
{ {
for (int i = 0; i < types.size(); ++i) for (int i = 0; i < types.size(); ++i)
if (types.getUnchecked(i)->createIdentifierString() == identifierString) if (types.getUnchecked(i)->matchesIdentifierString (identifierString))
return types.getUnchecked(i); return types.getUnchecked(i);
return nullptr; return nullptr;
@@ -259,15 +259,15 @@ struct PluginSorter
switch (method) switch (method)
{ {
case KnownPluginList::sortByCategory: diff = first->category.compareLexicographically (second->category); break; case KnownPluginList::sortByCategory: diff = first->category.compareNatural (second->category); break;
case KnownPluginList::sortByManufacturer: diff = first->manufacturerName.compareLexicographically (second->manufacturerName); break; case KnownPluginList::sortByManufacturer: diff = first->manufacturerName.compareNatural (second->manufacturerName); break;
case KnownPluginList::sortByFormat: diff = first->pluginFormatName.compare (second->pluginFormatName); break; case KnownPluginList::sortByFormat: diff = first->pluginFormatName.compare (second->pluginFormatName); break;
case KnownPluginList::sortByFileSystemLocation: diff = lastPathPart (first->fileOrIdentifier).compare (lastPathPart (second->fileOrIdentifier)); break; case KnownPluginList::sortByFileSystemLocation: diff = lastPathPart (first->fileOrIdentifier).compare (lastPathPart (second->fileOrIdentifier)); break;
default: break; default: break;
} }
if (diff == 0) if (diff == 0)
diff = first->name.compareLexicographically (second->name); diff = first->name.compareNatural (second->name);
return diff * direction; return diff * direction;
} }
@@ -75,11 +75,10 @@ class AudioDeviceSelectorComponent::MidiInputSelectorComponentListBox : public
private ListBoxModel private ListBoxModel
{ {
public: public:
MidiInputSelectorComponentListBox (AudioDeviceManager& dm, MidiInputSelectorComponentListBox (AudioDeviceManager& dm, const String& noItems)
const String& noItemsMessage_)
: ListBox (String::empty, nullptr), : ListBox (String::empty, nullptr),
deviceManager (dm), deviceManager (dm),
noItemsMessage (noItemsMessage_) noItemsMessage (noItems)
{ {
items = MidiInput::getDevices(); items = MidiInput::getDevices();
@@ -87,15 +86,12 @@ public:
setOutlineThickness (1); setOutlineThickness (1);
} }
int getNumRows() int getNumRows() override
{ {
return items.size(); return items.size();
} }
void paintListBoxItem (int row, void paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected) override
Graphics& g,
int width, int height,
bool rowIsSelected)
{ {
if (isPositiveAndBelow (row, items.size())) if (isPositiveAndBelow (row, items.size()))
{ {
@@ -118,7 +114,7 @@ public:
} }
} }
void listBoxItemClicked (int row, const MouseEvent& e) void listBoxItemClicked (int row, const MouseEvent& e) override
{ {
selectRow (row); selectRow (row);
@@ -126,12 +122,12 @@ public:
flipEnablement (row); flipEnablement (row);
} }
void listBoxItemDoubleClicked (int row, const MouseEvent&) void listBoxItemDoubleClicked (int row, const MouseEvent&) override
{ {
flipEnablement (row); flipEnablement (row);
} }
void returnKeyPressed (int row) void returnKeyPressed (int row) override
{ {
flipEnablement (row); flipEnablement (row);
} }
@@ -214,7 +210,6 @@ public:
type.scanForDevices(); type.scanForDevices();
setup.manager->addChangeListener (this); setup.manager->addChangeListener (this);
updateAllControls();
} }
~AudioDeviceSettingsPanel() ~AudioDeviceSettingsPanel()
@@ -224,83 +219,107 @@ public:
void resized() override void resized() override
{ {
const int lx = proportionOfWidth (0.35f); if (AudioDeviceSelectorComponent* parent = findParentComponentOfClass<AudioDeviceSelectorComponent>())
const int w = proportionOfWidth (0.4f);
const int h = 24;
const int space = 6;
const int dh = h + space;
int y = 0;
if (outputDeviceDropDown != nullptr)
{ {
outputDeviceDropDown->setBounds (lx, y, w, h); Rectangle<int> r (proportionOfWidth (0.35f), 0, proportionOfWidth (0.6f), 3000);
if (testButton != nullptr) const int maxListBoxHeight = 100;
testButton->setBounds (proportionOfWidth (0.77f), const int h = parent->getItemHeight();
outputDeviceDropDown->getY(), const int space = h / 4;
proportionOfWidth (0.18f),
h); if (outputDeviceDropDown != nullptr)
y += dh; {
Rectangle<int> row (r.removeFromTop (h));
if (testButton != nullptr)
{
testButton->changeWidthToFitText (h);
testButton->setBounds (row.removeFromRight (testButton->getWidth()));
row.removeFromRight (space);
}
outputDeviceDropDown->setBounds (row);
r.removeFromTop (space);
}
if (inputDeviceDropDown != nullptr)
{
Rectangle<int> row (r.removeFromTop (h));
inputLevelMeter->setBounds (row.removeFromRight (testButton != nullptr ? testButton->getWidth() : row.getWidth() / 6));
row.removeFromRight (space);
inputDeviceDropDown->setBounds (row);
r.removeFromTop (space);
}
if (outputChanList != nullptr)
{
outputChanList->setBounds (r.removeFromTop (outputChanList->getBestHeight (maxListBoxHeight)));
outputChanLabel->setBounds (0, outputChanList->getBounds().getCentreY() - h / 2, r.getX(), h);
r.removeFromTop (space);
}
if (inputChanList != nullptr)
{
inputChanList->setBounds (r.removeFromTop (inputChanList->getBestHeight (maxListBoxHeight)));
inputChanLabel->setBounds (0, inputChanList->getBounds().getCentreY() - h / 2, r.getX(), h);
r.removeFromTop (space);
}
r.removeFromTop (space * 2);
if (showAdvancedSettingsButton != nullptr)
{
showAdvancedSettingsButton->setBounds (r.withHeight (h));
showAdvancedSettingsButton->changeWidthToFitText();
}
const bool advancedSettingsVisible = showAdvancedSettingsButton == nullptr
|| ! showAdvancedSettingsButton->isVisible();
if (sampleRateDropDown != nullptr)
{
sampleRateDropDown->setVisible (advancedSettingsVisible);
sampleRateDropDown->setBounds (r.removeFromTop (h));
r.removeFromTop (space);
}
if (bufferSizeDropDown != nullptr)
{
bufferSizeDropDown->setVisible (advancedSettingsVisible);
bufferSizeDropDown->setBounds (r.removeFromTop (h));
r.removeFromTop (space);
}
r.removeFromTop (space);
if (showUIButton != nullptr || resetDeviceButton != nullptr)
{
Rectangle<int> buttons (r.removeFromTop (h));
if (showUIButton != nullptr)
{
showUIButton->setVisible (advancedSettingsVisible);
showUIButton->changeWidthToFitText (h);
showUIButton->setBounds (buttons.removeFromLeft (showUIButton->getWidth()));
buttons.removeFromLeft (space);
}
if (resetDeviceButton != nullptr)
{
resetDeviceButton->setVisible (advancedSettingsVisible);
resetDeviceButton->changeWidthToFitText (h);
resetDeviceButton->setBounds (buttons.removeFromLeft (resetDeviceButton->getWidth()));
}
r.removeFromTop (space);
}
setSize (getWidth(), r.getY());
} }
else
if (inputDeviceDropDown != nullptr)
{ {
inputDeviceDropDown->setBounds (lx, y, w, h); jassertfalse;
inputLevelMeter->setBounds (proportionOfWidth (0.77f),
inputDeviceDropDown->getY(),
proportionOfWidth (0.18f),
h);
y += dh;
}
const int maxBoxHeight = 100;
if (outputChanList != nullptr)
{
const int bh = outputChanList->getBestHeight (maxBoxHeight);
outputChanList->setBounds (lx, y, proportionOfWidth (0.55f), bh);
y += bh + space;
}
if (inputChanList != nullptr)
{
const int bh = inputChanList->getBestHeight (maxBoxHeight);
inputChanList->setBounds (lx, y, proportionOfWidth (0.55f), bh);
y += bh + space;
}
y += space * 2;
if (showAdvancedSettingsButton != nullptr)
{
showAdvancedSettingsButton->changeWidthToFitText (h);
showAdvancedSettingsButton->setTopLeftPosition (lx, y);
}
if (sampleRateDropDown != nullptr)
{
sampleRateDropDown->setVisible (showAdvancedSettingsButton == nullptr
|| ! showAdvancedSettingsButton->isVisible());
sampleRateDropDown->setBounds (lx, y, w, h);
y += dh;
}
if (bufferSizeDropDown != nullptr)
{
bufferSizeDropDown->setVisible (showAdvancedSettingsButton == nullptr
|| ! showAdvancedSettingsButton->isVisible());
bufferSizeDropDown->setBounds (lx, y, w, h);
y += dh;
}
if (showUIButton != nullptr)
{
showUIButton->setVisible (showAdvancedSettingsButton == nullptr
|| ! showAdvancedSettingsButton->isVisible());
showUIButton->changeWidthToFitText (h);
showUIButton->setTopLeftPosition (lx, y);
} }
} }
@@ -398,6 +417,10 @@ public:
{ {
setup.manager->playTestSound(); setup.manager->playTestSound();
} }
else if (button == resetDeviceButton)
{
resetDevice();
}
} }
void updateAllControls() void updateAllControls()
@@ -406,6 +429,7 @@ public:
updateInputsComboBox(); updateInputsComboBox();
updateControlPanelButton(); updateControlPanelButton();
updateResetButton();
if (AudioIODevice* const currentDevice = setup.manager->getCurrentAudioDevice()) if (AudioIODevice* const currentDevice = setup.manager->getCurrentAudioDevice())
{ {
@@ -418,6 +442,7 @@ public:
= new ChannelSelectorListBox (setup, ChannelSelectorListBox::audioOutputType, = new ChannelSelectorListBox (setup, ChannelSelectorListBox::audioOutputType,
TRANS ("(no audio output channels found)"))); TRANS ("(no audio output channels found)")));
outputChanLabel = new Label (String::empty, TRANS("Active output channels:")); outputChanLabel = new Label (String::empty, TRANS("Active output channels:"));
outputChanLabel->setJustificationType (Justification::centredRight);
outputChanLabel->attachToComponent (outputChanList, true); outputChanLabel->attachToComponent (outputChanList, true);
} }
@@ -438,6 +463,7 @@ public:
= new ChannelSelectorListBox (setup, ChannelSelectorListBox::audioInputType, = new ChannelSelectorListBox (setup, ChannelSelectorListBox::audioInputType,
TRANS("(no audio input channels found)"))); TRANS("(no audio input channels found)")));
inputChanLabel = new Label (String::empty, TRANS("Active input channels:")); inputChanLabel = new Label (String::empty, TRANS("Active input channels:"));
inputChanLabel->setJustificationType (Justification::centredRight);
inputChanLabel->attachToComponent (inputChanList, true); inputChanLabel->attachToComponent (inputChanList, true);
} }
@@ -468,6 +494,7 @@ public:
inputDeviceDropDown->setSelectedId (-1, dontSendNotification); inputDeviceDropDown->setSelectedId (-1, dontSendNotification);
} }
sendLookAndFeelChange();
resized(); resized();
setSize (getWidth(), getLowestY() + 4); setSize (getWidth(), getLowestY() + 4);
} }
@@ -477,6 +504,12 @@ public:
updateAllControls(); updateAllControls();
} }
void resetDevice()
{
setup.manager->closeAudioDevice();
setup.manager->restartLastAudioDevice();
}
private: private:
AudioIODeviceType& type; AudioIODeviceType& type;
const AudioDeviceSetupDetails setup; const AudioDeviceSetupDetails setup;
@@ -485,7 +518,7 @@ private:
ScopedPointer<Label> outputDeviceLabel, inputDeviceLabel, sampleRateLabel, bufferSizeLabel, inputChanLabel, outputChanLabel; ScopedPointer<Label> outputDeviceLabel, inputDeviceLabel, sampleRateLabel, bufferSizeLabel, inputChanLabel, outputChanLabel;
ScopedPointer<TextButton> testButton; ScopedPointer<TextButton> testButton;
ScopedPointer<Component> inputLevelMeter; ScopedPointer<Component> inputLevelMeter;
ScopedPointer<TextButton> showUIButton, showAdvancedSettingsButton; ScopedPointer<TextButton> showUIButton, showAdvancedSettingsButton, resetDeviceButton;
void showCorrectDeviceName (ComboBox* const box, const bool isInput) void showCorrectDeviceName (ComboBox* const box, const bool isInput)
{ {
@@ -532,7 +565,7 @@ private:
if (currentDevice != nullptr && currentDevice->hasControlPanel()) if (currentDevice != nullptr && currentDevice->hasControlPanel())
{ {
addAndMakeVisible (showUIButton = new TextButton (TRANS ("Show this device's control panel"), addAndMakeVisible (showUIButton = new TextButton (TRANS ("Control panel"),
TRANS ("Opens the device's own control panel"))); TRANS ("Opens the device's own control panel")));
showUIButton->addListener (this); showUIButton->addListener (this);
} }
@@ -540,6 +573,28 @@ private:
resized(); resized();
} }
void updateResetButton()
{
if (AudioIODevice* const currentDevice = setup.manager->getCurrentAudioDevice())
{
if (currentDevice->hasControlPanel())
{
if (resetDeviceButton == nullptr)
{
addAndMakeVisible (resetDeviceButton = new TextButton (TRANS ("Reset device"),
TRANS ("Resets the audio interface - sometimes needed after changing a device's properties in its custom control panel")));
resetDeviceButton->addListener (this);
resized();
}
return;
}
}
resetDeviceButton = nullptr;
}
void updateOutputsComboBox() void updateOutputsComboBox()
{ {
if (setup.maxNumOutputChannels > 0 || ! type.hasSeparateInputsAndOutputs()) if (setup.maxNumOutputChannels > 0 || ! type.hasSeparateInputsAndOutputs())
@@ -557,7 +612,8 @@ private:
if (setup.maxNumOutputChannels > 0) if (setup.maxNumOutputChannels > 0)
{ {
addAndMakeVisible (testButton = new TextButton (TRANS("Test"))); addAndMakeVisible (testButton = new TextButton (TRANS("Test"),
TRANS("Plays a test tone")));
testButton->addListener (this); testButton->addListener (this);
} }
} }
@@ -918,6 +974,7 @@ AudioDeviceSelectorComponent::AudioDeviceSelectorComponent (AudioDeviceManager&
const bool showChannelsAsStereoPairs_, const bool showChannelsAsStereoPairs_,
const bool hideAdvancedOptionsWithButton_) const bool hideAdvancedOptionsWithButton_)
: deviceManager (dm), : deviceManager (dm),
itemHeight (24),
minOutputChannels (minOutputChannels_), minOutputChannels (minOutputChannels_),
maxOutputChannels (maxOutputChannels_), maxOutputChannels (maxOutputChannels_),
minInputChannels (minInputChannels_), minInputChannels (minInputChannels_),
@@ -984,42 +1041,40 @@ AudioDeviceSelectorComponent::~AudioDeviceSelectorComponent()
deviceManager.removeChangeListener (this); deviceManager.removeChangeListener (this);
} }
void AudioDeviceSelectorComponent::setItemHeight (int newItemHeight)
{
itemHeight = newItemHeight;
resized();
}
void AudioDeviceSelectorComponent::resized() void AudioDeviceSelectorComponent::resized()
{ {
const int lx = proportionOfWidth (0.35f); Rectangle<int> r (proportionOfWidth (0.35f), 15, proportionOfWidth (0.6f), 3000);
const int w = proportionOfWidth (0.4f); const int space = itemHeight / 4;
const int h = 24;
const int space = 6;
const int dh = h + space;
int y = 15;
if (deviceTypeDropDown != nullptr) if (deviceTypeDropDown != nullptr)
{ {
deviceTypeDropDown->setBounds (lx, y, proportionOfWidth (0.3f), h); deviceTypeDropDown->setBounds (r.removeFromTop (itemHeight));
y += dh + space * 2; r.removeFromTop (space * 3);
} }
if (audioDeviceSettingsComp != nullptr) if (audioDeviceSettingsComp != nullptr)
{ {
audioDeviceSettingsComp->setBounds (0, y, getWidth(), audioDeviceSettingsComp->getHeight()); audioDeviceSettingsComp->resized();
y += audioDeviceSettingsComp->getHeight() + space; audioDeviceSettingsComp->setBounds (r.removeFromTop (audioDeviceSettingsComp->getHeight())
.withX (0).withWidth (getWidth()));
r.removeFromTop (space);
} }
if (midiInputsList != nullptr) if (midiInputsList != nullptr)
{ {
const int bh = midiInputsList->getBestHeight (jmin (h * 8, getHeight() - y - space - h)); midiInputsList->setBounds (r.removeFromTop (midiInputsList->getBestHeight (jmin (itemHeight * 8,
midiInputsList->setBounds (lx, y, w, bh); getHeight() - r.getY() - space - itemHeight))));
y += bh + space; r.removeFromTop (space);
} }
if (midiOutputSelector != nullptr) if (midiOutputSelector != nullptr)
midiOutputSelector->setBounds (lx, y, w, h); midiOutputSelector->setBounds (r.removeFromTop (itemHeight));
}
void AudioDeviceSelectorComponent::childBoundsChanged (Component* child)
{
if (child == audioDeviceSettingsComp)
resized();
} }
void AudioDeviceSelectorComponent::comboBoxChanged (ComboBox* comboBoxThatHasChanged) void AudioDeviceSelectorComponent::comboBoxChanged (ComboBox* comboBoxThatHasChanged)
@@ -1069,13 +1124,10 @@ void AudioDeviceSelectorComponent::updateAllControls()
details.maxNumOutputChannels = maxOutputChannels; details.maxNumOutputChannels = maxOutputChannels;
details.useStereoPairs = showChannelsAsStereoPairs; details.useStereoPairs = showChannelsAsStereoPairs;
audioDeviceSettingsComp = new AudioDeviceSettingsPanel (*type, details, hideAdvancedOptionsWithButton); AudioDeviceSettingsPanel* sp = new AudioDeviceSettingsPanel (*type, details, hideAdvancedOptionsWithButton);
audioDeviceSettingsComp = sp;
if (audioDeviceSettingsComp != nullptr) addAndMakeVisible (sp);
{ sp->updateAllControls();
addAndMakeVisible (audioDeviceSettingsComp);
audioDeviceSettingsComp->resized();
}
} }
} }
@@ -75,11 +75,15 @@ public:
/** The device manager that this component is controlling */ /** The device manager that this component is controlling */
AudioDeviceManager& deviceManager; AudioDeviceManager& deviceManager;
/** Sets the standard height used for items in the panel. */
void setItemHeight (int itemHeight);
/** Returns the standard height used for items in the panel. */
int getItemHeight() const noexcept { return itemHeight; }
//============================================================================== //==============================================================================
/** @internal */ /** @internal */
void resized() override; void resized() override;
/** @internal */
void childBoundsChanged (Component*) override;
private: private:
//============================================================================== //==============================================================================
@@ -87,6 +91,7 @@ private:
ScopedPointer<Label> deviceTypeDropDownLabel; ScopedPointer<Label> deviceTypeDropDownLabel;
ScopedPointer<Component> audioDeviceSettingsComp; ScopedPointer<Component> audioDeviceSettingsComp;
String audioDeviceSettingsCompType; String audioDeviceSettingsCompType;
int itemHeight;
const int minOutputChannels, maxOutputChannels, minInputChannels, maxInputChannels; const int minOutputChannels, maxOutputChannels, minInputChannels, maxInputChannels;
const bool showChannelsAsStereoPairs; const bool showChannelsAsStereoPairs;
const bool hideAdvancedOptionsWithButton; const bool hideAdvancedOptionsWithButton;
@@ -39,10 +39,10 @@ struct AudioThumbnail::MinMaxValue
inline char getMinValue() const noexcept { return values[0]; } inline char getMinValue() const noexcept { return values[0]; }
inline char getMaxValue() const noexcept { return values[1]; } inline char getMaxValue() const noexcept { return values[1]; }
inline void setFloat (const float newMin, const float newMax) noexcept inline void setFloat (Range<float> newRange) noexcept
{ {
values[0] = (char) jlimit (-128, 127, roundFloatToInt (newMin * 127.0f)); values[0] = (char) jlimit (-128, 127, roundFloatToInt (newRange.getStart() * 127.0f));
values[1] = (char) jlimit (-128, 127, roundFloatToInt (newMax * 127.0f)); values[1] = (char) jlimit (-128, 127, roundFloatToInt (newRange.getEnd() * 127.0f));
if (values[0] == values[1]) if (values[0] == values[1])
{ {
@@ -115,7 +115,7 @@ public:
} }
} }
void getLevels (int64 startSample, int numSamples, Array<float>& levels) void getLevels (int64 startSample, int numSamples, Array<Range<float> >& levels)
{ {
const ScopedLock sl (readerLock); const ScopedLock sl (readerLock);
@@ -132,11 +132,10 @@ public:
if (reader != nullptr) if (reader != nullptr)
{ {
float l[4] = { 0 }; if (levels.size() < (int) reader->numChannels)
reader->readMaxLevels (startSample, numSamples, l[0], l[1], l[2], l[3]); levels.insertMultiple (0, Range<float>(), (int) (reader->numChannels - levels.size()));
levels.clearQuick(); reader->readMaxLevels (startSample, numSamples, levels.getRawDataPointer(), (int) reader->numChannels);
levels.addArray ((const float*) l, 4);
lastReaderUseTime = Time::getMillisecondCounter(); lastReaderUseTime = Time::getMillisecondCounter();
} }
@@ -202,8 +201,8 @@ public:
private: private:
AudioThumbnail& owner; AudioThumbnail& owner;
ScopedPointer <InputSource> source; ScopedPointer<InputSource> source;
ScopedPointer <AudioFormatReader> reader; ScopedPointer<AudioFormatReader> reader;
CriticalSection readerLock; CriticalSection readerLock;
uint32 lastReaderUseTime; uint32 lastReaderUseTime;
@@ -230,23 +229,26 @@ private:
const int lastThumbIndex = sampleToThumbSample (startSample + numToDo); const int lastThumbIndex = sampleToThumbSample (startSample + numToDo);
const int numThumbSamps = lastThumbIndex - firstThumbIndex; const int numThumbSamps = lastThumbIndex - firstThumbIndex;
HeapBlock<MinMaxValue> levelData ((size_t) numThumbSamps * 2); HeapBlock<MinMaxValue> levelData ((size_t) numThumbSamps * numChannels);
MinMaxValue* levels[2] = { levelData, levelData + numThumbSamps }; HeapBlock<MinMaxValue*> levels (numChannels);
for (int i = 0; i < (int) numChannels; ++i)
levels[i] = levelData + i * numThumbSamps;
HeapBlock<Range<float> > levelsRead (numChannels);
for (int i = 0; i < numThumbSamps; ++i) for (int i = 0; i < numThumbSamps; ++i)
{ {
float lowestLeft, highestLeft, lowestRight, highestRight; reader->readMaxLevels ((firstThumbIndex + i) * owner.samplesPerThumbSample,
owner.samplesPerThumbSample, levelsRead, numChannels);
reader->readMaxLevels ((firstThumbIndex + i) * owner.samplesPerThumbSample, owner.samplesPerThumbSample, for (int j = 0; j < (int) numChannels; ++j)
lowestLeft, highestLeft, lowestRight, highestRight); levels[j][i].setFloat (levelsRead[j]);
levels[0][i].setFloat (lowestLeft, highestLeft);
levels[1][i].setFloat (lowestRight, highestRight);
} }
{ {
const ScopedUnlock su (readerLock); const ScopedUnlock su (readerLock);
owner.setLevels (levels, firstThumbIndex, 2, numThumbSamps); owner.setLevels (levels, firstThumbIndex, (int) numChannels, numThumbSamps);
} }
numSamplesFinished += numToDo; numSamplesFinished += numToDo;
@@ -342,7 +344,7 @@ public:
} }
private: private:
Array <MinMaxValue> data; Array<MinMaxValue> data;
int peakLevel; int peakLevel;
void ensureSize (const int thumbSamples) void ensureSize (const int thumbSamples)
@@ -391,6 +393,7 @@ public:
const MinMaxValue* cacheData = getData (channelNum, clip.getX() - area.getX()); const MinMaxValue* cacheData = getData (channelNum, clip.getX() - area.getX());
RectangleList<float> waveform; RectangleList<float> waveform;
waveform.ensureStorageAllocated (clip.getWidth());
float x = (float) clip.getX(); float x = (float) clip.getX();
@@ -414,7 +417,7 @@ public:
} }
private: private:
Array <MinMaxValue> data; Array<MinMaxValue> data;
double cachedStart, cachedTimePerPixel; double cachedStart, cachedTimePerPixel;
int numChannelsCached, numSamplesCached; int numChannelsCached, numSamplesCached;
bool cacheNeedsRefilling; bool cacheNeedsRefilling;
@@ -451,7 +454,7 @@ private:
if (timePerPixel * rate <= sampsPerThumbSample && levelData != nullptr) if (timePerPixel * rate <= sampsPerThumbSample && levelData != nullptr)
{ {
int sample = roundToInt (startTime * rate); int sample = roundToInt (startTime * rate);
Array<float> levels; Array<Range<float> > levels;
int i; int i;
for (i = 0; i < numSamples; ++i) for (i = 0; i < numSamples; ++i)
@@ -469,11 +472,10 @@ private:
{ {
levelData->getLevels (sample, jmax (1, nextSample - sample), levels); levelData->getLevels (sample, jmax (1, nextSample - sample), levels);
const int totalChans = jmin (levels.size() / 2, numChannelsCached); const int totalChans = jmin (levels.size(), numChannelsCached);
for (int chan = 0; chan < totalChans; ++chan) for (int chan = 0; chan < totalChans; ++chan)
getData (chan, i)->setFloat (levels.getUnchecked (chan * 2), getData (chan, i)->setFloat (levels.getReference (chan));
levels.getUnchecked (chan * 2 + 1));
} }
} }
@@ -713,8 +715,7 @@ void AudioThumbnail::addBlock (const int64 startSample, const AudioSampleBuffer&
for (int i = 0; i < numToDo; ++i) for (int i = 0; i < numToDo; ++i)
{ {
const int start = i * samplesPerThumbSample; const int start = i * samplesPerThumbSample;
Range<float> range (FloatVectorOperations::findMinAndMax (sourceData + start, jmin (samplesPerThumbSample, numSamples - start))); dest[i].setFloat (FloatVectorOperations::findMinAndMax (sourceData + start, jmin (samplesPerThumbSample, numSamples - start)));
dest[i].setFloat (range.getStart(), range.getEnd());
} }
} }
@@ -816,7 +816,7 @@ void MidiKeyboardComponent::timerCallback()
const Array<MouseInputSource>& mouseSources = Desktop::getInstance().getMouseSources(); const Array<MouseInputSource>& mouseSources = Desktop::getInstance().getMouseSources();
for (MouseInputSource* mi = mouseSources.begin(), * const e = mouseSources.end(); mi != e; ++mi) for (MouseInputSource* mi = mouseSources.begin(), * const e = mouseSources.end(); mi != e; ++mi)
updateNoteUnderMouse (getLocalPoint (nullptr, mi->getScreenPosition()), mi->isDragging(), mi->getIndex()); updateNoteUnderMouse (getLocalPoint (nullptr, mi->getScreenPosition()).roundToInt(), mi->isDragging(), mi->getIndex());
} }
} }
@@ -1,7 +1,7 @@
{ {
"id": "juce_audio_utils", "id": "juce_audio_utils",
"name": "JUCE extra audio utility classes", "name": "JUCE extra audio utility classes",
"version": "3.0.5", "version": "3.0.8",
"description": "Classes for audio-related GUI and miscellaneous tasks.", "description": "Classes for audio-related GUI and miscellaneous tasks.",
"website": "http://www.juce.com/juce", "website": "http://www.juce.com/juce",
"license": "GPL/Commercial", "license": "GPL/Commercial",
@@ -35,7 +35,7 @@ AbstractFifo::AbstractFifo (const int capacity) noexcept
AbstractFifo::~AbstractFifo() {} AbstractFifo::~AbstractFifo() {}
int AbstractFifo::getTotalSize() const noexcept { return bufferSize; } int AbstractFifo::getTotalSize() const noexcept { return bufferSize; }
int AbstractFifo::getFreeSpace() const noexcept { return bufferSize - getNumReady(); } int AbstractFifo::getFreeSpace() const noexcept { return bufferSize - getNumReady() - 1; }
int AbstractFifo::getNumReady() const noexcept int AbstractFifo::getNumReady() const noexcept
{ {
@@ -42,7 +42,7 @@
- it must be able to be relocated in memory by a memcpy without this causing any problems - so - it must be able to be relocated in memory by a memcpy without this causing any problems - so
objects whose functionality relies on external pointers or references to themselves can not be used. objects whose functionality relies on external pointers or references to themselves can not be used.
You can of course have an array of pointers to any kind of object, e.g. Array <MyClass*>, but if You can of course have an array of pointers to any kind of object, e.g. Array<MyClass*>, but if
you do this, the array doesn't take any ownership of the objects - see the OwnedArray class or the you do this, the array doesn't take any ownership of the objects - see the OwnedArray class or the
ReferenceCountedArray class for more powerful ways of holding lists of objects. ReferenceCountedArray class for more powerful ways of holding lists of objects.
@@ -65,8 +65,7 @@ private:
public: public:
//============================================================================== //==============================================================================
/** Creates an empty array. */ /** Creates an empty array. */
Array() noexcept Array() noexcept : numUsed (0)
: numUsed (0)
{ {
} }
@@ -31,7 +31,7 @@ DynamicObject::DynamicObject()
} }
DynamicObject::DynamicObject (const DynamicObject& other) DynamicObject::DynamicObject (const DynamicObject& other)
: properties (other.properties) : ReferenceCountedObject(), properties (other.properties)
{ {
} }
@@ -85,16 +85,9 @@ void DynamicObject::clear()
void DynamicObject::cloneAllProperties() void DynamicObject::cloneAllProperties()
{ {
for (LinkedListPointer<NamedValueSet::NamedValue>* i = &(properties.values);;) for (int i = properties.size(); --i >= 0;)
{ if (var* v = properties.getVarPointerAt (i))
if (NamedValueSet::NamedValue* const v = i->get()) *v = v->clone();
{
v->value = v->value.clone();
i = &(v->nextListItem);
}
else
break;
}
} }
DynamicObject::Ptr DynamicObject::clone() DynamicObject::Ptr DynamicObject::clone()
@@ -110,32 +103,27 @@ void DynamicObject::writeAsJSON (OutputStream& out, const int indentLevel, const
if (! allOnOneLine) if (! allOnOneLine)
out << newLine; out << newLine;
for (LinkedListPointer<NamedValueSet::NamedValue>* i = &(properties.values);;) const int numValues = properties.size();
for (int i = 0; i < numValues; ++i)
{ {
if (NamedValueSet::NamedValue* const v = i->get()) if (! allOnOneLine)
JSONFormatter::writeSpaces (out, indentLevel + JSONFormatter::indentSize);
out << '"';
JSONFormatter::writeString (out, properties.getName (i));
out << "\": ";
JSONFormatter::write (out, properties.getValueAt (i), indentLevel + JSONFormatter::indentSize, allOnOneLine);
if (i < numValues - 1)
{ {
if (! allOnOneLine) if (allOnOneLine)
JSONFormatter::writeSpaces (out, indentLevel + JSONFormatter::indentSize); out << ", ";
else
out << '"'; out << ',' << newLine;
JSONFormatter::writeString (out, v->name);
out << "\": ";
JSONFormatter::write (out, v->value, indentLevel + JSONFormatter::indentSize, allOnOneLine);
if (v->nextListItem.get() != nullptr)
{
if (allOnOneLine)
out << ", ";
else
out << ',' << newLine;
}
else if (! allOnOneLine)
out << newLine;
i = &(v->nextListItem);
} }
else else if (! allOnOneLine)
break; out << newLine;
} }
if (! allOnOneLine) if (! allOnOneLine)
@@ -58,7 +58,7 @@ public:
virtual bool hasProperty (const Identifier& propertyName) const; virtual bool hasProperty (const Identifier& propertyName) const;
/** Returns a named property. /** Returns a named property.
This returns a void if no such property exists. This returns var::null if no such property exists.
*/ */
virtual var getProperty (const Identifier& propertyName) const; virtual var getProperty (const Identifier& propertyName) const;
@@ -26,53 +26,37 @@
============================================================================== ==============================================================================
*/ */
NamedValueSet::NamedValue::NamedValue() noexcept struct NamedValueSet::NamedValue
{ {
} NamedValue() noexcept {}
NamedValue (Identifier n, const var& v) : name (n), value (v) {}
NamedValue (const NamedValue& other) : name (other.name), value (other.value) {}
inline NamedValueSet::NamedValue::NamedValue (Identifier n, const var& v) #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
: name (n), value (v) NamedValue (NamedValue&& other) noexcept
{ : name (static_cast<Identifier&&> (other.name)),
} value (static_cast<var&&> (other.value))
{
}
NamedValueSet::NamedValue::NamedValue (const NamedValue& other) NamedValue (Identifier n, var&& v) : name (n), value (static_cast<var&&> (v))
: name (other.name), value (other.value) {
{ }
}
NamedValueSet::NamedValue& NamedValueSet::NamedValue::operator= (const NamedValueSet::NamedValue& other) NamedValue& operator= (NamedValue&& other) noexcept
{ {
name = other.name; name = static_cast<Identifier&&> (other.name);
value = other.value; value = static_cast<var&&> (other.value);
return *this; return *this;
} }
#endif
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS bool operator== (const NamedValue& other) const noexcept { return name == other.name && value == other.value; }
NamedValueSet::NamedValue::NamedValue (NamedValue&& other) noexcept bool operator!= (const NamedValue& other) const noexcept { return ! operator== (other); }
: nextListItem (static_cast<LinkedListPointer<NamedValue>&&> (other.nextListItem)),
name (static_cast<Identifier&&> (other.name)),
value (static_cast<var&&> (other.value))
{
}
inline NamedValueSet::NamedValue::NamedValue (Identifier n, var&& v) Identifier name;
: name (n), value (static_cast<var&&> (v)) var value;
{ };
}
NamedValueSet::NamedValue& NamedValueSet::NamedValue::operator= (NamedValue&& other) noexcept
{
nextListItem = static_cast<LinkedListPointer<NamedValue>&&> (other.nextListItem);
name = static_cast<Identifier&&> (other.name);
value = static_cast<var&&> (other.value);
return *this;
}
#endif
bool NamedValueSet::NamedValue::operator== (const NamedValueSet::NamedValue& other) const noexcept
{
return name == other.name && value == other.value;
}
//============================================================================== //==============================================================================
NamedValueSet::NamedValueSet() noexcept NamedValueSet::NamedValueSet() noexcept
@@ -80,20 +64,20 @@ NamedValueSet::NamedValueSet() noexcept
} }
NamedValueSet::NamedValueSet (const NamedValueSet& other) NamedValueSet::NamedValueSet (const NamedValueSet& other)
: values (other.values)
{ {
values.addCopyOfList (other.values);
} }
NamedValueSet& NamedValueSet::operator= (const NamedValueSet& other) NamedValueSet& NamedValueSet::operator= (const NamedValueSet& other)
{ {
clear(); clear();
values.addCopyOfList (other.values); values = other.values;
return *this; return *this;
} }
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
NamedValueSet::NamedValueSet (NamedValueSet&& other) noexcept NamedValueSet::NamedValueSet (NamedValueSet&& other) noexcept
: values (static_cast <LinkedListPointer<NamedValue>&&> (other.values)) : values (static_cast <Array<NamedValue>&&> (other.values))
{ {
} }
@@ -104,31 +88,18 @@ NamedValueSet& NamedValueSet::operator= (NamedValueSet&& other) noexcept
} }
#endif #endif
NamedValueSet::~NamedValueSet() NamedValueSet::~NamedValueSet() noexcept
{ {
clear();
} }
void NamedValueSet::clear() void NamedValueSet::clear()
{ {
values.deleteAll(); values.clear();
} }
bool NamedValueSet::operator== (const NamedValueSet& other) const bool NamedValueSet::operator== (const NamedValueSet& other) const
{ {
const NamedValue* i1 = values; return values == other.values;
const NamedValue* i2 = other.values;
while (i1 != nullptr && i2 != nullptr)
{
if (! (*i1 == *i2))
return false;
i1 = i1->nextListItem;
i2 = i2->nextListItem;
}
return true;
} }
bool NamedValueSet::operator!= (const NamedValueSet& other) const bool NamedValueSet::operator!= (const NamedValueSet& other) const
@@ -141,16 +112,15 @@ int NamedValueSet::size() const noexcept
return values.size(); return values.size();
} }
const var& NamedValueSet::operator[] (Identifier name) const const var& NamedValueSet::operator[] (const Identifier& name) const noexcept
{ {
for (NamedValue* i = values; i != nullptr; i = i->nextListItem) if (const var* v = getVarPointer (name))
if (i->name == name) return *v;
return i->value;
return var::null; return var::null;
} }
var NamedValueSet::getWithDefault (Identifier name, const var& defaultReturnValue) const var NamedValueSet::getWithDefault (const Identifier& name, const var& defaultReturnValue) const
{ {
if (const var* const v = getVarPointer (name)) if (const var* const v = getVarPointer (name))
return *v; return *v;
@@ -158,9 +128,9 @@ var NamedValueSet::getWithDefault (Identifier name, const var& defaultReturnValu
return defaultReturnValue; return defaultReturnValue;
} }
var* NamedValueSet::getVarPointer (Identifier name) const noexcept var* NamedValueSet::getVarPointer (const Identifier& name) const noexcept
{ {
for (NamedValue* i = values; i != nullptr; i = i->nextListItem) for (NamedValue* e = values.end(), *i = values.begin(); i != e; ++i)
if (i->name == name) if (i->name == name)
return &(i->value); return &(i->value);
@@ -170,145 +140,121 @@ var* NamedValueSet::getVarPointer (Identifier name) const noexcept
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
bool NamedValueSet::set (Identifier name, var&& newValue) bool NamedValueSet::set (Identifier name, var&& newValue)
{ {
LinkedListPointer<NamedValue>* i = &values; if (var* const v = getVarPointer (name))
while (i->get() != nullptr)
{ {
NamedValue* const v = i->get(); if (v->equalsWithSameType (newValue))
return false;
if (v->name == name) *v = static_cast<var&&> (newValue);
{ return true;
if (v->value.equalsWithSameType (newValue))
return false;
v->value = static_cast <var&&> (newValue);
return true;
}
i = &(v->nextListItem);
} }
i->insertNext (new NamedValue (name, static_cast <var&&> (newValue))); values.add (NamedValue (name, static_cast<var&&> (newValue)));
return true; return true;
} }
#endif #endif
bool NamedValueSet::set (Identifier name, const var& newValue) bool NamedValueSet::set (Identifier name, const var& newValue)
{ {
LinkedListPointer<NamedValue>* i = &values; if (var* const v = getVarPointer (name))
while (i->get() != nullptr)
{ {
NamedValue* const v = i->get(); if (v->equalsWithSameType (newValue))
return false;
if (v->name == name) *v = newValue;
{ return true;
if (v->value.equalsWithSameType (newValue))
return false;
v->value = newValue;
return true;
}
i = &(v->nextListItem);
} }
i->insertNext (new NamedValue (name, newValue)); values.add (NamedValue (name, newValue));
return true; return true;
} }
bool NamedValueSet::contains (Identifier name) const bool NamedValueSet::contains (const Identifier& name) const noexcept
{ {
return getVarPointer (name) != nullptr; return getVarPointer (name) != nullptr;
} }
int NamedValueSet::indexOf (Identifier name) const noexcept int NamedValueSet::indexOf (const Identifier& name) const noexcept
{ {
int index = 0; const int numValues = values.size();
for (NamedValue* i = values; i != nullptr; i = i->nextListItem) for (int i = 0; i < numValues; ++i)
{ if (values.getReference(i).name == name)
if (i->name == name) return i;
return index;
++index;
}
return -1; return -1;
} }
bool NamedValueSet::remove (Identifier name) bool NamedValueSet::remove (const Identifier& name)
{ {
LinkedListPointer<NamedValue>* i = &values; const int numValues = values.size();
for (;;) for (int i = 0; i < numValues; ++i)
{ {
NamedValue* const v = i->get(); if (values.getReference(i).name == name)
if (v == nullptr)
break;
if (v->name == name)
{ {
delete i->removeNext(); values.remove (i);
return true; return true;
} }
i = &(v->nextListItem);
} }
return false; return false;
} }
Identifier NamedValueSet::getName (const int index) const Identifier NamedValueSet::getName (const int index) const noexcept
{ {
const NamedValue* const v = values[index]; if (isPositiveAndBelow (index, values.size()))
jassert (v != nullptr); return values.getReference (index).name;
return v->name;
jassertfalse;
return Identifier();
} }
const var& NamedValueSet::getValueAt (const int index) const const var& NamedValueSet::getValueAt (const int index) const noexcept
{ {
const NamedValue* const v = values[index]; if (isPositiveAndBelow (index, values.size()))
jassert (v != nullptr); return values.getReference (index).value;
return v->value;
jassertfalse;
return var::null;
}
var* NamedValueSet::getVarPointerAt (int index) const noexcept
{
if (isPositiveAndBelow (index, values.size()))
return &(values.getReference (index).value);
return nullptr;
} }
void NamedValueSet::setFromXmlAttributes (const XmlElement& xml) void NamedValueSet::setFromXmlAttributes (const XmlElement& xml)
{ {
clear(); values.clearQuick();
LinkedListPointer<NamedValue>::Appender appender (values);
const int numAtts = xml.getNumAttributes(); // xxx inefficient - should write an att iterator.. for (const XmlElement::XmlAttributeNode* att = xml.attributes; att != nullptr; att = att->nextListItem)
for (int i = 0; i < numAtts; ++i)
{ {
const String& name = xml.getAttributeName (i); if (att->name.toString().startsWith ("base64:"))
const String& value = xml.getAttributeValue (i);
if (name.startsWith ("base64:"))
{ {
MemoryBlock mb; MemoryBlock mb;
if (mb.fromBase64Encoding (value)) if (mb.fromBase64Encoding (att->value))
{ {
appender.append (new NamedValue (name.substring (7), var (mb))); values.add (NamedValue (att->name.toString().substring (7), var (mb)));
continue; continue;
} }
} }
appender.append (new NamedValue (name, var (value))); values.add (NamedValue (att->name, var (att->value)));
} }
} }
void NamedValueSet::copyToXmlAttributes (XmlElement& xml) const void NamedValueSet::copyToXmlAttributes (XmlElement& xml) const
{ {
for (NamedValue* i = values; i != nullptr; i = i->nextListItem) for (NamedValue* e = values.end(), *i = values.begin(); i != e; ++i)
{ {
if (const MemoryBlock* mb = i->value.getBinaryData()) if (const MemoryBlock* mb = i->value.getBinaryData())
{ {
xml.setAttribute ("base64:" + i->name.toString(), xml.setAttribute ("base64:" + i->name.toString(), mb->toBase64Encoding());
mb->toBase64Encoding());
} }
else else
{ {
@@ -54,7 +54,7 @@ public:
#endif #endif
/** Destructor. */ /** Destructor. */
~NamedValueSet(); ~NamedValueSet() noexcept;
bool operator== (const NamedValueSet&) const; bool operator== (const NamedValueSet&) const;
bool operator!= (const NamedValueSet&) const; bool operator!= (const NamedValueSet&) const;
@@ -67,12 +67,12 @@ public:
If the name isn't found, this will return a void variant. If the name isn't found, this will return a void variant.
@see getProperty @see getProperty
*/ */
const var& operator[] (Identifier name) const; const var& operator[] (const Identifier& name) const noexcept;
/** Tries to return the named value, but if no such value is found, this will /** Tries to return the named value, but if no such value is found, this will
instead return the supplied default value. instead return the supplied default value.
*/ */
var getWithDefault (Identifier name, const var& defaultReturnValue) const; var getWithDefault (const Identifier& name, const var& defaultReturnValue) const;
/** Changes or adds a named value. /** Changes or adds a named value.
@returns true if a value was changed or added; false if the @returns true if a value was changed or added; false if the
@@ -89,38 +89,42 @@ public:
#endif #endif
/** Returns true if the set contains an item with the specified name. */ /** Returns true if the set contains an item with the specified name. */
bool contains (Identifier name) const; bool contains (const Identifier& name) const noexcept;
/** Removes a value from the set. /** Removes a value from the set.
@returns true if a value was removed; false if there was no value @returns true if a value was removed; false if there was no value
with the name that was given. with the name that was given.
*/ */
bool remove (Identifier name); bool remove (const Identifier& name);
/** Returns the name of the value at a given index. /** Returns the name of the value at a given index.
The index must be between 0 and size() - 1. The index must be between 0 and size() - 1.
*/ */
Identifier getName (int index) const; Identifier getName (int index) const noexcept;
/** Returns the value of the item at a given index.
The index must be between 0 and size() - 1.
*/
const var& getValueAt (int index) const;
/** Returns the index of the given name, or -1 if it's not found. */
int indexOf (Identifier name) const noexcept;
/** Removes all values. */
void clear();
//==============================================================================
/** Returns a pointer to the var that holds a named value, or null if there is /** Returns a pointer to the var that holds a named value, or null if there is
no value with this name. no value with this name.
Do not use this method unless you really need access to the internal var object Do not use this method unless you really need access to the internal var object
for some reason - for normal reading and writing always prefer operator[]() and set(). for some reason - for normal reading and writing always prefer operator[]() and set().
*/ */
var* getVarPointer (Identifier name) const noexcept; var* getVarPointer (const Identifier& name) const noexcept;
/** Returns the value of the item at a given index.
The index must be between 0 and size() - 1.
*/
const var& getValueAt (int index) const noexcept;
/** Returns the value of the item at a given index.
The index must be between 0 and size() - 1, or this will return a nullptr
*/
var* getVarPointerAt (int index) const noexcept;
/** Returns the index of the given name, or -1 if it's not found. */
int indexOf (const Identifier& name) const noexcept;
/** Removes all values. */
void clear();
//============================================================================== //==============================================================================
/** Sets properties to the values of all of an XML element's attributes. */ /** Sets properties to the values of all of an XML element's attributes. */
@@ -133,32 +137,8 @@ public:
private: private:
//============================================================================== //==============================================================================
class NamedValue struct NamedValue;
{ Array<NamedValue> values;
public:
NamedValue() noexcept;
NamedValue (const NamedValue&);
NamedValue (Identifier, const var&);
NamedValue& operator= (const NamedValue&);
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
NamedValue (NamedValue&&) noexcept;
NamedValue (Identifier, var&&);
NamedValue& operator= (NamedValue&&) noexcept;
#endif
bool operator== (const NamedValue&) const noexcept;
LinkedListPointer<NamedValue> nextListItem;
Identifier name;
var value;
private:
JUCE_LEAK_DETECTOR (NamedValue)
};
friend class LinkedListPointer<NamedValue>;
LinkedListPointer<NamedValue> values;
friend class DynamicObject;
}; };
@@ -830,7 +830,7 @@ public:
This will use a comparator object to sort the elements into order. The object This will use a comparator object to sort the elements into order. The object
passed must have a method of the form: passed must have a method of the form:
@code @code
int compareElements (ElementType first, ElementType second); int compareElements (ElementType* first, ElementType* second);
@endcode @endcode
..and this method must return: ..and this method must return:
@@ -38,7 +38,7 @@
The template parameter specifies the class of the object you want to point to - the easiest The template parameter specifies the class of the object you want to point to - the easiest
way to make a class reference-countable is to simply make it inherit from ReferenceCountedObject way to make a class reference-countable is to simply make it inherit from ReferenceCountedObject
or SingleThreadedReferenceCountedObject, but if you need to, you can roll your own reference-countable or SingleThreadedReferenceCountedObject, but if you need to, you can roll your own reference-countable
class by implementing a set of mathods called incReferenceCount(), decReferenceCount(), and class by implementing a set of methods called incReferenceCount(), decReferenceCount(), and
decReferenceCountWithoutDeleting(). See ReferenceCountedObject for examples of how these methods decReferenceCountWithoutDeleting(). See ReferenceCountedObject for examples of how these methods
should behave. should behave.
@@ -72,7 +72,7 @@ public:
const ScopedLockType lock (other.getLock()); const ScopedLockType lock (other.getLock());
numUsed = other.size(); numUsed = other.size();
data.setAllocatedSize (numUsed); data.setAllocatedSize (numUsed);
memcpy (data.elements, other.getRawDataPointer(), numUsed * sizeof (ObjectClass*)); memcpy (data.elements, other.getRawDataPointer(), (size_t) numUsed * sizeof (ObjectClass*));
for (int i = numUsed; --i >= 0;) for (int i = numUsed; --i >= 0;)
if (ObjectClass* o = data.elements[i]) if (ObjectClass* o = data.elements[i])
@@ -476,7 +476,7 @@ bool File::loadFileAsData (MemoryBlock& destBlock) const
return false; return false;
FileInputStream in (*this); FileInputStream in (*this);
return in.openedOk() && getSize() == in.readIntoMemoryBlock (destBlock); return in.openedOk() && getSize() == (int64) in.readIntoMemoryBlock (destBlock);
} }
String File::loadFileAsString() const String File::loadFileAsString() const
@@ -361,6 +361,14 @@ public:
*/ */
File getLinkedTarget() const; File getLinkedTarget() const;
/** Returns a unique identifier for the file, if one is available.
Depending on the OS and file-system, this may be a unix inode number or
a win32 file identifier, or 0 if it fails to find one. The number will
be unique on the filesystem, but not globally.
*/
uint64 getFileIdentifier() const;
//============================================================================== //==============================================================================
/** Returns the last modification time of this file. /** Returns the last modification time of this file.
@@ -836,6 +844,11 @@ public:
/** In a plugin, this will return the path of the host executable. */ /** In a plugin, this will return the path of the host executable. */
hostApplicationPath, hostApplicationPath,
#if JUCE_WINDOWS
/** On a Windows machine, returns the location of the Windows/System32 folder. */
windowsSystemDirectory,
#endif
/** The directory in which applications normally get installed. /** The directory in which applications normally get installed.
So on windows, this would be something like "c:\program files", on the So on windows, this would be something like "c:\program files", on the
Mac "/Applications", or "/usr" on linux. Mac "/Applications", or "/usr" on linux.
@@ -28,41 +28,34 @@
int64 juce_fileSetPosition (void* handle, int64 pos); int64 juce_fileSetPosition (void* handle, int64 pos);
//============================================================================== //==============================================================================
FileInputStream::FileInputStream (const File& f) FileInputStream::FileInputStream (const File& f)
: file (f), : file (f),
fileHandle (nullptr), fileHandle (nullptr),
currentPosition (0), currentPosition (0),
status (Result::ok()), status (Result::ok())
needToSeek (true)
{ {
openHandle(); openHandle();
} }
FileInputStream::~FileInputStream()
{
closeHandle();
}
//==============================================================================
int64 FileInputStream::getTotalLength() int64 FileInputStream::getTotalLength()
{ {
// You should always check that a stream opened successfully before using it!
jassert (openedOk());
return file.getSize(); return file.getSize();
} }
int FileInputStream::read (void* buffer, int bytesToRead) int FileInputStream::read (void* buffer, int bytesToRead)
{ {
// You should always check that a stream opened successfully before using it!
jassert (openedOk()); jassert (openedOk());
// The buffer should never be null, and a negative size is probably a
// sign that something is broken!
jassert (buffer != nullptr && bytesToRead >= 0); jassert (buffer != nullptr && bytesToRead >= 0);
if (needToSeek)
{
if (juce_fileSetPosition (fileHandle, currentPosition) < 0)
return 0;
needToSeek = false;
}
const size_t num = readInternal (buffer, (size_t) bytesToRead); const size_t num = readInternal (buffer, (size_t) bytesToRead);
currentPosition += num; currentPosition += num;
@@ -81,15 +74,11 @@ int64 FileInputStream::getPosition()
bool FileInputStream::setPosition (int64 pos) bool FileInputStream::setPosition (int64 pos)
{ {
// You should always check that a stream opened successfully before using it!
jassert (openedOk()); jassert (openedOk());
if (pos != currentPosition) if (pos != currentPosition)
{ currentPosition = juce_fileSetPosition (fileHandle, pos);
pos = jlimit ((int64) 0, getTotalLength(), pos);
needToSeek |= (currentPosition != pos); return currentPosition == pos;
currentPosition = pos;
}
return true;
} }
@@ -40,10 +40,11 @@ class JUCE_API FileInputStream : public InputStream
{ {
public: public:
//============================================================================== //==============================================================================
/** Creates a FileInputStream. /** Creates a FileInputStream to read from the given file.
@param fileToRead the file to read from - if the file can't be accessed for some After creating a FileInputStream, you should use openedOk() or failedToOpen()
reason, then the stream will just contain no data to make sure that it's OK before trying to read from it! If it failed, you
can call getStatus() to get more error information.
*/ */
explicit FileInputStream (const File& fileToRead); explicit FileInputStream (const File& fileToRead);
@@ -73,24 +74,23 @@ public:
//============================================================================== //==============================================================================
int64 getTotalLength() override; int64 getTotalLength() override;
int read (void* destBuffer, int maxBytesToRead) override; int read (void*, int) override;
bool isExhausted() override; bool isExhausted() override;
int64 getPosition() override; int64 getPosition() override;
bool setPosition (int64 pos) override; bool setPosition (int64) override;
private: private:
//============================================================================== //==============================================================================
File file; const File file;
void* fileHandle; void* fileHandle;
int64 currentPosition; int64 currentPosition;
Result status; Result status;
bool needToSeek;
void openHandle(); void openHandle();
void closeHandle(); size_t readInternal (void*, size_t);
size_t readInternal (void* buffer, size_t numBytes);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileInputStream) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileInputStream)
}; };
#endif // JUCE_FILEINPUTSTREAM_H_INCLUDED #endif // JUCE_FILEINPUTSTREAM_H_INCLUDED
@@ -26,32 +26,7 @@
============================================================================== ==============================================================================
*/ */
WildcardFileFilter::WildcardFileFilter (const String& fileWildcardPatterns, static void parseWildcard (const String& pattern, StringArray& result)
const String& directoryWildcardPatterns,
const String& desc)
: FileFilter (desc.isEmpty() ? fileWildcardPatterns
: (desc + " (" + fileWildcardPatterns + ")"))
{
parse (fileWildcardPatterns, fileWildcards);
parse (directoryWildcardPatterns, directoryWildcards);
}
WildcardFileFilter::~WildcardFileFilter()
{
}
bool WildcardFileFilter::isFileSuitable (const File& file) const
{
return match (file, fileWildcards);
}
bool WildcardFileFilter::isDirectorySuitable (const File& file) const
{
return match (file, directoryWildcards);
}
//==============================================================================
void WildcardFileFilter::parse (const String& pattern, StringArray& result)
{ {
result.addTokens (pattern.toLowerCase(), ";,", "\"'"); result.addTokens (pattern.toLowerCase(), ";,", "\"'");
@@ -65,7 +40,7 @@ void WildcardFileFilter::parse (const String& pattern, StringArray& result)
result.set (i, "*"); result.set (i, "*");
} }
bool WildcardFileFilter::match (const File& file, const StringArray& wildcards) static bool matchWildcard (const File& file, const StringArray& wildcards)
{ {
const String filename (file.getFileName()); const String filename (file.getFileName());
@@ -75,3 +50,27 @@ bool WildcardFileFilter::match (const File& file, const StringArray& wildcards)
return false; return false;
} }
WildcardFileFilter::WildcardFileFilter (const String& fileWildcardPatterns,
const String& directoryWildcardPatterns,
const String& desc)
: FileFilter (desc.isEmpty() ? fileWildcardPatterns
: (desc + " (" + fileWildcardPatterns + ")"))
{
parseWildcard (fileWildcardPatterns, fileWildcards);
parseWildcard (directoryWildcardPatterns, directoryWildcards);
}
WildcardFileFilter::~WildcardFileFilter()
{
}
bool WildcardFileFilter::isFileSuitable (const File& file) const
{
return matchWildcard (file, fileWildcards);
}
bool WildcardFileFilter::isDirectorySuitable (const File& file) const
{
return matchWildcard (file, directoryWildcards);
}
@@ -75,12 +75,8 @@ private:
//============================================================================== //==============================================================================
StringArray fileWildcards, directoryWildcards; StringArray fileWildcards, directoryWildcards;
static void parse (const String& pattern, StringArray& result);
static bool match (const File& file, const StringArray& wildcards);
JUCE_LEAK_DETECTOR (WildcardFileFilter) JUCE_LEAK_DETECTOR (WildcardFileFilter)
}; };
#endif // JUCE_WILDCARDFILEFILTER_H_INCLUDED #endif // JUCE_WILDCARDFILEFILTER_H_INCLUDED
@@ -243,9 +243,9 @@ private:
if (r.failed()) if (r.failed())
return r; return r;
const String propertyName (propertyNameVar.toString()); const Identifier propertyName (propertyNameVar.toString());
if (propertyName.isNotEmpty()) if (propertyName.isValid())
{ {
t = t.findEndOfWhitespace(); t = t.findEndOfWhitespace();
oldT = t; oldT = t;
@@ -102,6 +102,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
static bool isNumericOrUndefined (const var& v) { return v.isInt() || v.isDouble() || v.isInt64() || v.isBool() || v.isUndefined(); } static bool isNumericOrUndefined (const var& v) { return v.isInt() || v.isDouble() || v.isInt64() || v.isBool() || v.isUndefined(); }
static int64 getOctalValue (const String& s) { BigInteger b; b.parseString (s, 8); return b.toInt64(); } static int64 getOctalValue (const String& s) { BigInteger b; b.parseString (s, 8); return b.toInt64(); }
static Identifier getPrototypeIdentifier() { static const Identifier i ("prototype"); return i; } static Identifier getPrototypeIdentifier() { static const Identifier i ("prototype"); return i; }
static var* getPropertyPointer (DynamicObject* o, Identifier i) { return o->getProperties().getVarPointer (i); }
//============================================================================== //==============================================================================
struct CodeLocation struct CodeLocation
@@ -139,13 +140,13 @@ struct JavascriptEngine::RootObject : public DynamicObject
{ {
if (DynamicObject* o = targetObject.getDynamicObject()) if (DynamicObject* o = targetObject.getDynamicObject())
{ {
if (var* prop = o->getProperties().getVarPointer (functionName)) if (const var* prop = getPropertyPointer (o, functionName))
return *prop; return *prop;
for (DynamicObject* p = o->getProperty (getPrototypeIdentifier()).getDynamicObject(); p != nullptr; for (DynamicObject* p = o->getProperty (getPrototypeIdentifier()).getDynamicObject(); p != nullptr;
p = p->getProperty (getPrototypeIdentifier()).getDynamicObject()) p = p->getProperty (getPrototypeIdentifier()).getDynamicObject())
{ {
if (var* prop = p->getProperties().getVarPointer (functionName)) if (const var* prop = getPropertyPointer (p, functionName))
return *prop; return *prop;
} }
} }
@@ -168,14 +169,14 @@ struct JavascriptEngine::RootObject : public DynamicObject
var* findRootClassProperty (Identifier className, Identifier propName) const var* findRootClassProperty (Identifier className, Identifier propName) const
{ {
if (DynamicObject* cls = root->getProperty (className).getDynamicObject()) if (DynamicObject* cls = root->getProperty (className).getDynamicObject())
return cls->getProperties().getVarPointer (propName); return getPropertyPointer (cls, propName);
return nullptr; return nullptr;
} }
var findSymbolInParentScopes (Identifier name) const var findSymbolInParentScopes (Identifier name) const
{ {
if (var* v = scope->getProperties().getVarPointer (name)) if (const var* v = getPropertyPointer (scope, name))
return *v; return *v;
return parent != nullptr ? parent->findSymbolInParentScopes (name) return parent != nullptr ? parent->findSymbolInParentScopes (name)
@@ -184,13 +185,11 @@ struct JavascriptEngine::RootObject : public DynamicObject
bool findAndInvokeMethod (Identifier function, const var::NativeFunctionArgs& args, var& result) const bool findAndInvokeMethod (Identifier function, const var::NativeFunctionArgs& args, var& result) const
{ {
const NamedValueSet& props = scope->getProperties();
DynamicObject* target = args.thisObject.getDynamicObject(); DynamicObject* target = args.thisObject.getDynamicObject();
if (target == nullptr || target == scope) if (target == nullptr || target == scope)
{ {
if (const var* m = props.getVarPointer (function)) if (const var* m = getPropertyPointer (scope, function))
{ {
if (FunctionObject* fo = dynamic_cast<FunctionObject*> (m->getObject())) if (FunctionObject* fo = dynamic_cast<FunctionObject*> (m->getObject()))
{ {
@@ -200,6 +199,8 @@ struct JavascriptEngine::RootObject : public DynamicObject
} }
} }
const NamedValueSet& props = scope->getProperties();
for (int i = 0; i < props.size(); ++i) for (int i = 0; i < props.size(); ++i)
if (DynamicObject* o = props.getValueAt (i).getDynamicObject()) if (DynamicObject* o = props.getValueAt (i).getDynamicObject())
if (Scope (this, root, o).findAndInvokeMethod (function, args, result)) if (Scope (this, root, o).findAndInvokeMethod (function, args, result))
@@ -353,7 +354,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
void assign (const Scope& s, const var& newValue) const override void assign (const Scope& s, const var& newValue) const override
{ {
if (var* v = s.scope->getProperties().getVarPointer (name)) if (var* v = getPropertyPointer (s.scope, name))
*v = newValue; *v = newValue;
else else
s.root->setProperty (name, newValue); s.root->setProperty (name, newValue);
@@ -378,7 +379,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
} }
if (DynamicObject* o = p.getDynamicObject()) if (DynamicObject* o = p.getDynamicObject())
if (var* v = o->getProperties().getVarPointer (child)) if (const var* v = getPropertyPointer (o, child))
return *v; return *v;
return var::undefined(); return var::undefined();
@@ -543,14 +544,14 @@ struct JavascriptEngine::RootObject : public DynamicObject
struct DivideOp : public BinaryOperator struct DivideOp : public BinaryOperator
{ {
DivideOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::divide) {} DivideOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::divide) {}
var getWithDoubles (double a, double b) const override { return a / b; } var getWithDoubles (double a, double b) const override { return b != 0 ? a / b : std::numeric_limits<double>::infinity(); }
var getWithInts (int64 a, int64 b) const override { return a / b; } var getWithInts (int64 a, int64 b) const override { return b != 0 ? var (a / (double) b) : var (std::numeric_limits<double>::infinity()); }
}; };
struct ModuloOp : public BinaryOperator struct ModuloOp : public BinaryOperator
{ {
ModuloOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::modulo) {} ModuloOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::modulo) {}
var getWithInts (int64 a, int64 b) const override { return a % b; } var getWithInts (int64 a, int64 b) const override { return b != 0 ? var (a % b) : var (std::numeric_limits<double>::infinity()); }
}; };
struct BitwiseOrOp : public BinaryOperator struct BitwiseOrOp : public BinaryOperator
@@ -768,7 +769,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
{ {
FunctionObject() noexcept {} FunctionObject() noexcept {}
FunctionObject (const FunctionObject& other) : functionCode (other.functionCode) FunctionObject (const FunctionObject& other) : DynamicObject(), functionCode (other.functionCode)
{ {
ExpressionTreeBuilder tb (functionCode); ExpressionTreeBuilder tb (functionCode);
tb.parseFunctionParamsAndBody (*this); tb.parseFunctionParamsAndBody (*this);
@@ -843,7 +844,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
String::CharPointerType end (p); String::CharPointerType end (p);
while (isIdentifierBody (*++end)) {} while (isIdentifierBody (*++end)) {}
const size_t len = end - p; const size_t len = (size_t) (end - p);
#define JUCE_JS_COMPARE_KEYWORD(name, str) if (len == sizeof (str) - 1 && matchToken (TokenTypes::name, len)) return TokenTypes::name; #define JUCE_JS_COMPARE_KEYWORD(name, str) if (len == sizeof (str) - 1 && matchToken (TokenTypes::name, len)) return TokenTypes::name;
JUCE_JS_KEYWORDS (JUCE_JS_COMPARE_KEYWORD) JUCE_JS_KEYWORDS (JUCE_JS_COMPARE_KEYWORD)
@@ -1145,8 +1146,14 @@ struct JavascriptEngine::RootObject : public DynamicObject
match (TokenTypes::semicolon); match (TokenTypes::semicolon);
} }
s->iterator = parseExpression(); if (matchIf (TokenTypes::closeParen))
match (TokenTypes::closeParen); s->iterator = new Statement (location);
else
{
s->iterator = parseExpression();
match (TokenTypes::closeParen);
}
s->body = parseStatement(); s->body = parseStatement();
return s.release(); return s.release();
} }
@@ -1555,6 +1562,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
setMethod ("log", Math_log); setMethod ("log10", Math_log10); setMethod ("log", Math_log); setMethod ("log10", Math_log10);
setMethod ("exp", Math_exp); setMethod ("pow", Math_pow); setMethod ("exp", Math_exp); setMethod ("pow", Math_pow);
setMethod ("sqr", Math_sqr); setMethod ("sqrt", Math_sqrt); setMethod ("sqr", Math_sqr); setMethod ("sqrt", Math_sqrt);
setMethod ("ceil", Math_ceil); setMethod ("floor", Math_floor);
} }
static var Math_pi (Args) { return double_Pi; } static var Math_pi (Args) { return double_Pi; }
@@ -1587,6 +1595,8 @@ struct JavascriptEngine::RootObject : public DynamicObject
static var Math_pow (Args a) { return pow (getDouble (a, 0), getDouble (a, 1)); } static var Math_pow (Args a) { return pow (getDouble (a, 0), getDouble (a, 1)); }
static var Math_sqr (Args a) { double x = getDouble (a, 0); return x * x; } static var Math_sqr (Args a) { double x = getDouble (a, 0); return x * x; }
static var Math_sqrt (Args a) { return std::sqrt (getDouble (a, 0)); } static var Math_sqrt (Args a) { return std::sqrt (getDouble (a, 0)); }
static var Math_ceil (Args a) { return std::ceil (getDouble (a, 0)); }
static var Math_floor (Args a) { return std::floor (getDouble (a, 0)); }
static Identifier getClassName() { static const Identifier i ("Math"); return i; } static Identifier getClassName() { static const Identifier i ("Math"); return i; }
template <typename Type> static Type sign (Type n) noexcept { return n > 0 ? (Type) 1 : (n < 0 ? (Type) -1 : 0); } template <typename Type> static Type sign (Type n) noexcept { return n > 0 ? (Type) 1 : (n < 0 ? (Type) -1 : 0); }
@@ -1649,7 +1659,7 @@ JavascriptEngine::JavascriptEngine() : maximumExecutionTime (15.0), root (new R
JavascriptEngine::~JavascriptEngine() {} JavascriptEngine::~JavascriptEngine() {}
void JavascriptEngine::prepareTimeout() const { root->timeout = Time::getCurrentTime() + maximumExecutionTime; } void JavascriptEngine::prepareTimeout() const noexcept { root->timeout = Time::getCurrentTime() + maximumExecutionTime; }
void JavascriptEngine::registerNativeObject (Identifier name, DynamicObject* object) void JavascriptEngine::registerNativeObject (Identifier name, DynamicObject* object)
{ {
@@ -1705,6 +1715,11 @@ var JavascriptEngine::callFunction (Identifier function, const var::NativeFuncti
return returnVal; return returnVal;
} }
const NamedValueSet& JavascriptEngine::getRootObjectProperties() const noexcept
{
return root->getProperties();
}
#if JUCE_MSVC #if JUCE_MSVC
#pragma warning (pop) #pragma warning (pop)
#endif #endif
@@ -96,10 +96,13 @@ public:
*/ */
RelativeTime maximumExecutionTime; RelativeTime maximumExecutionTime;
/** Provides access to the set of properties of the root namespace object. */
const NamedValueSet& getRootObjectProperties() const noexcept;
private: private:
JUCE_PUBLIC_IN_DLL_BUILD (struct RootObject) JUCE_PUBLIC_IN_DLL_BUILD (struct RootObject)
ReferenceCountedObjectPtr<RootObject> root; const ReferenceCountedObjectPtr<RootObject> root;
void prepareTimeout() const; void prepareTimeout() const noexcept;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JavascriptEngine) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JavascriptEngine)
}; };
@@ -53,6 +53,8 @@
#if JUCE_WINDOWS #if JUCE_WINDOWS
#include <ctime> #include <ctime>
#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
@@ -79,6 +81,7 @@
#if JUCE_LINUX #if JUCE_LINUX
#include <langinfo.h> #include <langinfo.h>
#include <ifaddrs.h>
#endif #endif
#include <pwd.h> #include <pwd.h>
@@ -194,6 +194,7 @@ extern JUCE_API void JUCE_CALLTYPE logAssertion (const char* file, int line) noe
#include "threads/juce_ScopedLock.h" #include "threads/juce_ScopedLock.h"
#include "threads/juce_CriticalSection.h" #include "threads/juce_CriticalSection.h"
#include "maths/juce_Range.h" #include "maths/juce_Range.h"
#include "maths/juce_NormalisableRange.h"
#include "containers/juce_ElementComparator.h" #include "containers/juce_ElementComparator.h"
#include "containers/juce_ArrayAllocationBase.h" #include "containers/juce_ArrayAllocationBase.h"
#include "containers/juce_Array.h" #include "containers/juce_Array.h"
@@ -244,6 +245,7 @@ extern JUCE_API void JUCE_CALLTYPE logAssertion (const char* file, int line) noe
#include "misc/juce_Uuid.h" #include "misc/juce_Uuid.h"
#include "misc/juce_WindowsRegistry.h" #include "misc/juce_WindowsRegistry.h"
#include "system/juce_PlatformDefs.h" #include "system/juce_PlatformDefs.h"
#include "system/juce_CompilerSupport.h"
#include "system/juce_SystemStats.h" #include "system/juce_SystemStats.h"
#include "threads/juce_ChildProcess.h" #include "threads/juce_ChildProcess.h"
#include "threads/juce_DynamicLibrary.h" #include "threads/juce_DynamicLibrary.h"
@@ -1,7 +1,7 @@
{ {
"id": "juce_core", "id": "juce_core",
"name": "JUCE core classes", "name": "JUCE core classes",
"version": "3.0.5", "version": "3.0.8",
"description": "The essential set of basic JUCE classes, as required by all the other JUCE modules. Includes text, container, memory, threading and i/o functionality.", "description": "The essential set of basic JUCE classes, as required by all the other JUCE modules. Includes text, container, memory, threading and i/o functionality.",
"website": "http://www.juce.com/juce", "website": "http://www.juce.com/juce",
"license": "ISC Permissive", "license": "ISC Permissive",
@@ -307,41 +307,28 @@ void BigInteger::negate() noexcept
negative = (! negative) && ! isZero(); negative = (! negative) && ! isZero();
} }
#if JUCE_USE_INTRINSICS && ! defined (__INTEL_COMPILER) #if JUCE_USE_MSVC_INTRINSICS && ! defined (__INTEL_COMPILER)
#pragma intrinsic (_BitScanReverse) #pragma intrinsic (_BitScanReverse)
#endif #endif
namespace BitFunctions inline static int highestBitInInt (uint32 n) noexcept
{ {
inline int countBitsInInt32 (uint32 n) noexcept jassert (n != 0); // (the built-in functions may not work for n = 0)
{
n -= ((n >> 1) & 0x55555555);
n = (((n >> 2) & 0x33333333) + (n & 0x33333333));
n = (((n >> 4) + n) & 0x0f0f0f0f);
n += (n >> 8);
n += (n >> 16);
return (int) (n & 0x3f);
}
inline int highestBitInInt (uint32 n) noexcept #if JUCE_GCC
{ return 31 - __builtin_clz (n);
jassert (n != 0); // (the built-in functions may not work for n = 0) #elif JUCE_USE_MSVC_INTRINSICS
unsigned long highest;
#if JUCE_GCC _BitScanReverse (&highest, n);
return 31 - __builtin_clz (n); return (int) highest;
#elif JUCE_USE_INTRINSICS #else
unsigned long highest; n |= (n >> 1);
_BitScanReverse (&highest, n); n |= (n >> 2);
return (int) highest; n |= (n >> 4);
#else n |= (n >> 8);
n |= (n >> 1); n |= (n >> 16);
n |= (n >> 2); return countBitsInInt32 (n >> 1);
n |= (n >> 4); #endif
n |= (n >> 8);
n |= (n >> 16);
return countBitsInInt32 (n >> 1);
#endif
}
} }
int BigInteger::countNumberOfSetBits() const noexcept int BigInteger::countNumberOfSetBits() const noexcept
@@ -349,7 +336,7 @@ int BigInteger::countNumberOfSetBits() const noexcept
int total = 0; int total = 0;
for (int i = (int) bitToIndex (highestBit) + 1; --i >= 0;) for (int i = (int) bitToIndex (highestBit) + 1; --i >= 0;)
total += BitFunctions::countBitsInInt32 (values[i]); total += countNumberOfBits (values[i]);
return total; return total;
} }
@@ -361,7 +348,7 @@ int BigInteger::getHighestBit() const noexcept
const uint32 n = values[i]; const uint32 n = values[i];
if (n != 0) if (n != 0)
return BitFunctions::highestBitInInt (n) + (i << 5); return highestBitInInt (n) + (i << 5);
} }
return -1; return -1;
@@ -364,6 +364,11 @@ inline int roundToInt (const FloatType value) noexcept
#endif #endif
} }
inline int roundToInt (int value) noexcept
{
return value;
}
#if JUCE_MSVC #if JUCE_MSVC
#ifndef __INTEL_COMPILER #ifndef __INTEL_COMPILER
#pragma float_control (pop) #pragma float_control (pop)
@@ -438,6 +443,23 @@ inline int nextPowerOfTwo (int n) noexcept
return n + 1; return n + 1;
} }
/** Returns the number of bits in a 32-bit integer. */
inline int countNumberOfBits (uint32 n) noexcept
{
n -= ((n >> 1) & 0x55555555);
n = (((n >> 2) & 0x33333333) + (n & 0x33333333));
n = (((n >> 4) + n) & 0x0f0f0f0f);
n += (n >> 8);
n += (n >> 16);
return (int) (n & 0x3f);
}
/** Returns the number of bits in a 64-bit integer. */
inline int countNumberOfBits (uint64 n) noexcept
{
return countNumberOfBits ((uint32) n) + countNumberOfBits ((uint32) (n >> 32));
}
/** Performs a modulo operation, but can cope with the dividend being negative. /** Performs a modulo operation, but can cope with the dividend being negative.
The divisor must be greater than zero. The divisor must be greater than zero.
*/ */
@@ -77,6 +77,13 @@ public:
: Range (position2, position1); : Range (position2, position1);
} }
/** Returns a range with a given start and length. */
static Range withStartAndLength (const ValueType startValue, const ValueType length) noexcept
{
jassert (length >= ValueType());
return Range (startValue, startValue + length);
}
/** Returns a range with the specified start position and a length of zero. */ /** Returns a range with the specified start position and a length of zero. */
static Range emptyRange (const ValueType start) noexcept static Range emptyRange (const ValueType start) noexcept
{ {
@@ -218,7 +218,7 @@ private:
#else #else
#define JUCE_ATOMICS_WINDOWS 1 // Windows with intrinsics #define JUCE_ATOMICS_WINDOWS 1 // Windows with intrinsics
#if JUCE_USE_INTRINSICS #if JUCE_USE_MSVC_INTRINSICS
#ifndef __INTEL_COMPILER #ifndef __INTEL_COMPILER
#pragma intrinsic (_InterlockedExchange, _InterlockedIncrement, _InterlockedDecrement, _InterlockedCompareExchange, \ #pragma intrinsic (_InterlockedExchange, _InterlockedIncrement, _InterlockedDecrement, _InterlockedCompareExchange, \
_InterlockedCompareExchange64, _InterlockedExchangeAdd, _ReadWriteBarrier) _InterlockedCompareExchange64, _InterlockedExchangeAdd, _ReadWriteBarrier)
@@ -87,16 +87,16 @@ public:
//============================================================================== //==============================================================================
/** Converts 3 little-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */ /** Converts 3 little-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */
static int littleEndian24Bit (const char* bytes) noexcept; static int littleEndian24Bit (const void* bytes) noexcept;
/** Converts 3 big-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */ /** Converts 3 big-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */
static int bigEndian24Bit (const char* bytes) noexcept; static int bigEndian24Bit (const void* bytes) noexcept;
/** Copies a 24-bit number to 3 little-endian bytes. */ /** Copies a 24-bit number to 3 little-endian bytes. */
static void littleEndian24BitToChars (int value, char* destBytes) noexcept; static void littleEndian24BitToChars (int value, void* destBytes) noexcept;
/** Copies a 24-bit number to 3 big-endian bytes. */ /** Copies a 24-bit number to 3 big-endian bytes. */
static void bigEndian24BitToChars (int value, char* destBytes) noexcept; static void bigEndian24BitToChars (int value, void* destBytes) noexcept;
//============================================================================== //==============================================================================
/** Returns true if the current CPU is big-endian. */ /** Returns true if the current CPU is big-endian. */
@@ -110,16 +110,16 @@ private:
//============================================================================== //==============================================================================
#if JUCE_USE_INTRINSICS && ! defined (__INTEL_COMPILER) #if JUCE_USE_MSVC_INTRINSICS && ! defined (__INTEL_COMPILER)
#pragma intrinsic (_byteswap_ulong) #pragma intrinsic (_byteswap_ulong)
#endif #endif
inline uint16 ByteOrder::swap (uint16 n) noexcept inline uint16 ByteOrder::swap (uint16 n) noexcept
{ {
#if JUCE_USE_INTRINSICSxxx // agh - the MS compiler has an internal error when you try to use this intrinsic! #if JUCE_USE_MSVC_INTRINSICSxxx // agh - the MS compiler has an internal error when you try to use this intrinsic!
return static_cast <uint16> (_byteswap_ushort (n)); return static_cast<uint16> (_byteswap_ushort (n));
#else #else
return static_cast <uint16> ((n << 8) | (n >> 8)); return static_cast<uint16> ((n << 8) | (n >> 8));
#endif #endif
} }
@@ -130,7 +130,7 @@ inline uint32 ByteOrder::swap (uint32 n) noexcept
#elif JUCE_GCC && JUCE_INTEL && ! JUCE_NO_INLINE_ASM #elif JUCE_GCC && JUCE_INTEL && ! JUCE_NO_INLINE_ASM
asm("bswap %%eax" : "=a"(n) : "a"(n)); asm("bswap %%eax" : "=a"(n) : "a"(n));
return n; return n;
#elif JUCE_USE_INTRINSICS #elif JUCE_USE_MSVC_INTRINSICS
return _byteswap_ulong (n); return _byteswap_ulong (n);
#elif JUCE_MSVC && ! JUCE_NO_INLINE_ASM #elif JUCE_MSVC && ! JUCE_NO_INLINE_ASM
__asm { __asm {
@@ -150,7 +150,7 @@ inline uint64 ByteOrder::swap (uint64 value) noexcept
{ {
#if JUCE_MAC || JUCE_IOS #if JUCE_MAC || JUCE_IOS
return OSSwapInt64 (value); return OSSwapInt64 (value);
#elif JUCE_USE_INTRINSICS #elif JUCE_USE_MSVC_INTRINSICS
return _byteswap_uint64 (value); return _byteswap_uint64 (value);
#else #else
return (((int64) swap ((uint32) value)) << 32) | swap ((uint32) (value >> 32)); return (((int64) swap ((uint32) value)) << 32) | swap ((uint32) (value >> 32));
@@ -164,12 +164,12 @@ inline uint64 ByteOrder::swap (uint64 value) noexcept
inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) noexcept { return swap (v); } inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) noexcept { return swap (v); }
inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) noexcept { return swap (v); } inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) noexcept { return swap (v); }
inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) noexcept { return swap (v); } inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) noexcept { return swap (v); }
inline uint32 ByteOrder::littleEndianInt (const void* const bytes) noexcept { return *static_cast <const uint32*> (bytes); } inline uint32 ByteOrder::littleEndianInt (const void* const bytes) noexcept { return *static_cast<const uint32*> (bytes); }
inline uint64 ByteOrder::littleEndianInt64 (const void* const bytes) noexcept { return *static_cast <const uint64*> (bytes); } inline uint64 ByteOrder::littleEndianInt64 (const void* const bytes) noexcept { return *static_cast<const uint64*> (bytes); }
inline uint16 ByteOrder::littleEndianShort (const void* const bytes) noexcept { return *static_cast <const uint16*> (bytes); } inline uint16 ByteOrder::littleEndianShort (const void* const bytes) noexcept { return *static_cast<const uint16*> (bytes); }
inline uint32 ByteOrder::bigEndianInt (const void* const bytes) noexcept { return swap (*static_cast <const uint32*> (bytes)); } inline uint32 ByteOrder::bigEndianInt (const void* const bytes) noexcept { return swap (*static_cast<const uint32*> (bytes)); }
inline uint64 ByteOrder::bigEndianInt64 (const void* const bytes) noexcept { return swap (*static_cast <const uint64*> (bytes)); } inline uint64 ByteOrder::bigEndianInt64 (const void* const bytes) noexcept { return swap (*static_cast<const uint64*> (bytes)); }
inline uint16 ByteOrder::bigEndianShort (const void* const bytes) noexcept { return swap (*static_cast <const uint16*> (bytes)); } inline uint16 ByteOrder::bigEndianShort (const void* const bytes) noexcept { return swap (*static_cast<const uint16*> (bytes)); }
inline bool ByteOrder::isBigEndian() noexcept { return false; } inline bool ByteOrder::isBigEndian() noexcept { return false; }
#else #else
inline uint16 ByteOrder::swapIfBigEndian (const uint16 v) noexcept { return swap (v); } inline uint16 ByteOrder::swapIfBigEndian (const uint16 v) noexcept { return swap (v); }
@@ -178,19 +178,19 @@ inline uint64 ByteOrder::swap (uint64 value) noexcept
inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) noexcept { return v; } inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) noexcept { return v; }
inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) noexcept { return v; } inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) noexcept { return v; }
inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) noexcept { return v; } inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) noexcept { return v; }
inline uint32 ByteOrder::littleEndianInt (const void* const bytes) noexcept { return swap (*static_cast <const uint32*> (bytes)); } inline uint32 ByteOrder::littleEndianInt (const void* const bytes) noexcept { return swap (*static_cast<const uint32*> (bytes)); }
inline uint64 ByteOrder::littleEndianInt64 (const void* const bytes) noexcept { return swap (*static_cast <const uint64*> (bytes)); } inline uint64 ByteOrder::littleEndianInt64 (const void* const bytes) noexcept { return swap (*static_cast<const uint64*> (bytes)); }
inline uint16 ByteOrder::littleEndianShort (const void* const bytes) noexcept { return swap (*static_cast <const uint16*> (bytes)); } inline uint16 ByteOrder::littleEndianShort (const void* const bytes) noexcept { return swap (*static_cast<const uint16*> (bytes)); }
inline uint32 ByteOrder::bigEndianInt (const void* const bytes) noexcept { return *static_cast <const uint32*> (bytes); } inline uint32 ByteOrder::bigEndianInt (const void* const bytes) noexcept { return *static_cast<const uint32*> (bytes); }
inline uint64 ByteOrder::bigEndianInt64 (const void* const bytes) noexcept { return *static_cast <const uint64*> (bytes); } inline uint64 ByteOrder::bigEndianInt64 (const void* const bytes) noexcept { return *static_cast<const uint64*> (bytes); }
inline uint16 ByteOrder::bigEndianShort (const void* const bytes) noexcept { return *static_cast <const uint16*> (bytes); } inline uint16 ByteOrder::bigEndianShort (const void* const bytes) noexcept { return *static_cast<const uint16*> (bytes); }
inline bool ByteOrder::isBigEndian() noexcept { return true; } inline bool ByteOrder::isBigEndian() noexcept { return true; }
#endif #endif
inline int ByteOrder::littleEndian24Bit (const char* const bytes) noexcept { return (((int) bytes[2]) << 16) | (((int) (uint8) bytes[1]) << 8) | ((int) (uint8) bytes[0]); } inline int ByteOrder::littleEndian24Bit (const void* const bytes) noexcept { return (((int) static_cast<const int8*> (bytes)[2]) << 16) | (((int) static_cast<const uint8*> (bytes)[1]) << 8) | ((int) static_cast<const uint8*> (bytes)[0]); }
inline int ByteOrder::bigEndian24Bit (const char* const bytes) noexcept { return (((int) bytes[0]) << 16) | (((int) (uint8) bytes[1]) << 8) | ((int) (uint8) bytes[2]); } inline int ByteOrder::bigEndian24Bit (const void* const bytes) noexcept { return (((int) static_cast<const int8*> (bytes)[0]) << 16) | (((int) static_cast<const uint8*> (bytes)[1]) << 8) | ((int) static_cast<const uint8*> (bytes)[2]); }
inline void ByteOrder::littleEndian24BitToChars (const int value, char* const destBytes) noexcept { destBytes[0] = (char)(value & 0xff); destBytes[1] = (char)((value >> 8) & 0xff); destBytes[2] = (char)((value >> 16) & 0xff); } inline void ByteOrder::littleEndian24BitToChars (const int value, void* const destBytes) noexcept { static_cast<uint8*> (destBytes)[0] = (uint8) value; static_cast<uint8*> (destBytes)[1] = (uint8) (value >> 8); static_cast<uint8*> (destBytes)[2] = (uint8) (value >> 16); }
inline void ByteOrder::bigEndian24BitToChars (const int value, char* const destBytes) noexcept { destBytes[0] = (char)((value >> 16) & 0xff); destBytes[1] = (char)((value >> 8) & 0xff); destBytes[2] = (char)(value & 0xff); } inline void ByteOrder::bigEndian24BitToChars (const int value, void* const destBytes) noexcept { static_cast<uint8*> (destBytes)[0] = (uint8) (value >> 16); static_cast<uint8*> (destBytes)[1] = (uint8) (value >> 8); static_cast<uint8*> (destBytes)[2] = (uint8) value; }
#endif // JUCE_BYTEORDER_H_INCLUDED #endif // JUCE_BYTEORDER_H_INCLUDED
@@ -88,14 +88,14 @@ MemoryBlock& MemoryBlock::operator= (const MemoryBlock& other)
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
MemoryBlock::MemoryBlock (MemoryBlock&& other) noexcept MemoryBlock::MemoryBlock (MemoryBlock&& other) noexcept
: data (static_cast <HeapBlock<char>&&> (other.data)), : data (static_cast<HeapBlock<char>&&> (other.data)),
size (other.size) size (other.size)
{ {
} }
MemoryBlock& MemoryBlock::operator= (MemoryBlock&& other) noexcept MemoryBlock& MemoryBlock::operator= (MemoryBlock&& other) noexcept
{ {
data = static_cast <HeapBlock<char>&&> (other.data); data = static_cast<HeapBlock<char>&&> (other.data);
size = other.size; size = other.size;
return *this; return *this;
} }
@@ -346,7 +346,7 @@ void MemoryBlock::loadFromHexString (StringRef hex)
if (c == 0) if (c == 0)
{ {
setSize (static_cast <size_t> (dest - data)); setSize (static_cast<size_t> (dest - data));
return; return;
} }
} }
@@ -208,7 +208,7 @@ private:
The template parameter specifies the class of the object you want to point to - the easiest The template parameter specifies the class of the object you want to point to - the easiest
way to make a class reference-countable is to simply make it inherit from ReferenceCountedObject way to make a class reference-countable is to simply make it inherit from ReferenceCountedObject
or SingleThreadedReferenceCountedObject, but if you need to, you can roll your own reference-countable or SingleThreadedReferenceCountedObject, but if you need to, you can roll your own reference-countable
class by implementing a set of mathods called incReferenceCount(), decReferenceCount(), and class by implementing a set of methods called incReferenceCount(), decReferenceCount(), and
decReferenceCountWithoutDeleting(). See ReferenceCountedObject for examples of how these methods decReferenceCountWithoutDeleting(). See ReferenceCountedObject for examples of how these methods
should behave. should behave.
@@ -182,11 +182,11 @@ public:
/** Swaps this object with that of another ScopedPointer. /** Swaps this object with that of another ScopedPointer.
The two objects simply exchange their pointers. The two objects simply exchange their pointers.
*/ */
void swapWith (ScopedPointer <ObjectType>& other) noexcept void swapWith (ScopedPointer<ObjectType>& other) noexcept
{ {
// Two ScopedPointers should never be able to refer to the same object - if // Two ScopedPointers should never be able to refer to the same object - if
// this happens, you must have done something dodgy! // this happens, you must have done something dodgy!
jassert (object != other.object || this == other.getAddress()); jassert (object != other.object || this == other.getAddress() || object == nullptr);
std::swap (object, other.object); std::swap (object, other.object);
} }
@@ -231,7 +231,7 @@ private:
template <class ObjectType> template <class ObjectType>
bool operator== (const ScopedPointer<ObjectType>& pointer1, ObjectType* const pointer2) noexcept bool operator== (const ScopedPointer<ObjectType>& pointer1, ObjectType* const pointer2) noexcept
{ {
return static_cast <ObjectType*> (pointer1) == pointer2; return static_cast<ObjectType*> (pointer1) == pointer2;
} }
/** Compares a ScopedPointer with another pointer. /** Compares a ScopedPointer with another pointer.
@@ -240,7 +240,7 @@ bool operator== (const ScopedPointer<ObjectType>& pointer1, ObjectType* const po
template <class ObjectType> template <class ObjectType>
bool operator!= (const ScopedPointer<ObjectType>& pointer1, ObjectType* const pointer2) noexcept bool operator!= (const ScopedPointer<ObjectType>& pointer1, ObjectType* const pointer2) noexcept
{ {
return static_cast <ObjectType*> (pointer1) != pointer2; return static_cast<ObjectType*> (pointer1) != pointer2;
} }
//============================================================================== //==============================================================================
@@ -139,7 +139,7 @@ private:
static SharedObjectHolder& getSharedObjectHolder() noexcept static SharedObjectHolder& getSharedObjectHolder() noexcept
{ {
static char holder [sizeof (SharedObjectHolder)] = { 0 }; static void* holder [(sizeof (SharedObjectHolder) + sizeof(void*) - 1) / sizeof(void*)] = { 0 };
return *reinterpret_cast<SharedObjectHolder*> (holder); return *reinterpret_cast<SharedObjectHolder*> (holder);
} }
@@ -158,7 +158,7 @@ public:
public: public:
Master() noexcept {} Master() noexcept {}
~Master() ~Master() noexcept
{ {
// You must remember to call clear() in your source object's destructor! See the notes // You must remember to call clear() in your source object's destructor! See the notes
// for the WeakReference class for an example of how to do this. // for the WeakReference class for an example of how to do this.
@@ -187,7 +187,7 @@ public:
to zero all the references to this object that may be out there. See the WeakReference to zero all the references to this object that may be out there. See the WeakReference
class notes for an example of how to do this. class notes for an example of how to do this.
*/ */
void clear() void clear() noexcept
{ {
if (sharedPointer != nullptr) if (sharedPointer != nullptr)
sharedPointer->clearPointer(); sharedPointer->clearPointer();
@@ -399,16 +399,21 @@ public final class JuceAppActivity extends Activity
private native void handleKeyDown (long host, int keycode, int textchar); private native void handleKeyDown (long host, int keycode, int textchar);
private native void handleKeyUp (long host, int keycode, int textchar); private native void handleKeyUp (long host, int keycode, int textchar);
public void showKeyboard (boolean shouldShow) public void showKeyboard (String type)
{ {
InputMethodManager imm = (InputMethodManager) getSystemService (Context.INPUT_METHOD_SERVICE); InputMethodManager imm = (InputMethodManager) getSystemService (Context.INPUT_METHOD_SERVICE);
if (imm != null) if (imm != null)
{ {
if (shouldShow) if (type.length() > 0)
imm.showSoftInput (this, InputMethodManager.SHOW_FORCED); {
imm.showSoftInput (this, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT);
imm.setInputMethod (getWindowToken(), type);
}
else else
{
imm.hideSoftInputFromWindow (getWindowToken(), 0); imm.hideSoftInputFromWindow (getWindowToken(), 0);
}
} }
} }
@@ -271,26 +271,33 @@ public:
JNIEnv* attach() noexcept JNIEnv* attach() noexcept
{ {
if (JNIEnv* env = attachToCurrentThread()) if (android.activity != nullptr)
{ {
SpinLock::ScopedLockType sl (addRemoveLock); if (JNIEnv* env = attachToCurrentThread())
return addEnv (env); {
SpinLock::ScopedLockType sl (addRemoveLock);
return addEnv (env);
}
jassertfalse;
} }
jassertfalse;
return nullptr; return nullptr;
} }
void detach() noexcept void detach() noexcept
{ {
jvm->DetachCurrentThread(); if (android.activity != nullptr)
{
jvm->DetachCurrentThread();
const pthread_t thisThread = pthread_self(); const pthread_t thisThread = pthread_self();
SpinLock::ScopedLockType sl (addRemoveLock); SpinLock::ScopedLockType sl (addRemoveLock);
for (int i = 0; i < maxThreads; ++i) for (int i = 0; i < maxThreads; ++i)
if (threads[i] == thisThread) if (threads[i] == thisThread)
threads[i] = 0; threads[i] = 0;
}
} }
JNIEnv* getOrAttach() noexcept JNIEnv* getOrAttach() noexcept
@@ -355,6 +362,12 @@ private:
extern ThreadLocalJNIEnvHolder threadLocalJNIEnvHolder; extern ThreadLocalJNIEnvHolder threadLocalJNIEnvHolder;
struct AndroidThreadScope
{
AndroidThreadScope() { threadLocalJNIEnvHolder.attach(); }
~AndroidThreadScope() { threadLocalJNIEnvHolder.detach(); }
};
//============================================================================== //==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (createNewView, "createNewView", "(ZJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;") \ METHOD (createNewView, "createNewView", "(ZJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;") \

Some files were not shown because too many files have changed in this diff Show More