diff --git a/JaySynth.jucer b/JaySynth.jucer
new file mode 100644
index 0000000..8c8cfbf
--- /dev/null
+++ b/JaySynth.jucer
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/JuceLibraryCode/AppConfig.h b/JuceLibraryCode/AppConfig.h
index 713d916..e927a5f 100644
--- a/JuceLibraryCode/AppConfig.h
+++ b/JuceLibraryCode/AppConfig.h
@@ -11,8 +11,8 @@
*/
-#ifndef __JUCE_APPCONFIG_SGFP29__
-#define __JUCE_APPCONFIG_SGFP29__
+#ifndef __JUCE_APPCONFIG_FGWBAQ__
+#define __JUCE_APPCONFIG_FGWBAQ__
//==============================================================================
// [BEGIN_USER_CODE_SECTION]
@@ -84,6 +84,10 @@
//#define JUCE_USE_MP3AUDIOFORMAT
#endif
+#ifndef JUCE_USE_LAME_AUDIO_FORMAT
+ //#define JUCE_USE_LAME_AUDIO_FORMAT
+#endif
+
#ifndef JUCE_USE_WINDOWS_MEDIA_FORMAT
//#define JUCE_USE_WINDOWS_MEDIA_FORMAT
#endif
@@ -95,6 +99,10 @@
#define JUCE_PLUGINHOST_VST 1
#endif
+#ifndef JUCE_PLUGINHOST_VST3
+ //#define JUCE_PLUGINHOST_VST3
+#endif
+
#ifndef JUCE_PLUGINHOST_AU
//#define JUCE_PLUGINHOST_AU
#endif
@@ -118,6 +126,10 @@
//#define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES
#endif
+#ifndef JUCE_INCLUDE_ZLIB_CODE
+ //#define JUCE_INCLUDE_ZLIB_CODE
+#endif
+
//==============================================================================
// juce_graphics flags:
@@ -126,7 +138,7 @@
#endif
#ifndef JUCE_USE_DIRECTWRITE
- #define JUCE_USE_DIRECTWRITE 0
+ //#define JUCE_USE_DIRECTWRITE
#endif
//==============================================================================
@@ -155,6 +167,10 @@
//#define JUCE_WEB_BROWSER
#endif
+#ifndef JUCE_ENABLE_LIVE_CONSTANT_EDITOR
+ //#define JUCE_ENABLE_LIVE_CONSTANT_EDITOR
+#endif
+
//==============================================================================
// Audio plugin settings..
@@ -162,26 +178,38 @@
#ifndef JucePlugin_Build_VST
#define JucePlugin_Build_VST 1
#endif
+#ifndef JucePlugin_Build_VST3
+ #define JucePlugin_Build_VST3 0
+#endif
#ifndef JucePlugin_Build_AU
- #define JucePlugin_Build_AU 1
+ #define JucePlugin_Build_AU 0
#endif
#ifndef JucePlugin_Build_RTAS
#define JucePlugin_Build_RTAS 0
#endif
+#ifndef JucePlugin_Build_AAX
+ #define JucePlugin_Build_AAX 0
+#endif
#ifndef JucePlugin_Name
#define JucePlugin_Name "JaySynth"
#endif
#ifndef JucePlugin_Desc
- #define JucePlugin_Desc "A Synthesizer VST-Plugin"
+ #define JucePlugin_Desc "JaySynth"
#endif
#ifndef JucePlugin_Manufacturer
#define JucePlugin_Manufacturer "Jay Digital Illusions"
#endif
+#ifndef JucePlugin_ManufacturerWebsite
+ #define JucePlugin_ManufacturerWebsite ""
+#endif
+#ifndef JucePlugin_ManufacturerEmail
+ #define JucePlugin_ManufacturerEmail ""
+#endif
#ifndef JucePlugin_ManufacturerCode
- #define JucePlugin_ManufacturerCode '3110'
+ #define JucePlugin_ManufacturerCode 'Manu'
#endif
#ifndef JucePlugin_PluginCode
- #define JucePlugin_PluginCode 'Jsy1'
+ #define JucePlugin_PluginCode 'Plug'
#endif
#ifndef JucePlugin_MaxNumInputChannels
#define JucePlugin_MaxNumInputChannels 2
@@ -190,7 +218,7 @@
#define JucePlugin_MaxNumOutputChannels 2
#endif
#ifndef JucePlugin_PreferredChannelConfigurations
- #define JucePlugin_PreferredChannelConfigurations {2, 2}
+ #define JucePlugin_PreferredChannelConfigurations {1, 1}, {2, 2}
#endif
#ifndef JucePlugin_IsSynth
#define JucePlugin_IsSynth 1
@@ -204,20 +232,17 @@
#ifndef JucePlugin_SilenceInProducesSilenceOut
#define JucePlugin_SilenceInProducesSilenceOut 0
#endif
-#ifndef JucePlugin_TailLengthSeconds
- #define JucePlugin_TailLengthSeconds 0
-#endif
#ifndef JucePlugin_EditorRequiresKeyboardFocus
#define JucePlugin_EditorRequiresKeyboardFocus 0
#endif
#ifndef JucePlugin_Version
- #define JucePlugin_Version 0,5,6,0
+ #define JucePlugin_Version 1.0.0
#endif
#ifndef JucePlugin_VersionCode
- #define JucePlugin_VersionCode 560
+ #define JucePlugin_VersionCode 0x10000
#endif
#ifndef JucePlugin_VersionString
- #define JucePlugin_VersionString "0.5.6"
+ #define JucePlugin_VersionString "1.0.0"
#endif
#ifndef JucePlugin_VSTUniqueID
#define JucePlugin_VSTUniqueID JucePlugin_PluginCode
@@ -241,10 +266,7 @@
#define JucePlugin_AUManufacturerCode JucePlugin_ManufacturerCode
#endif
#ifndef JucePlugin_CFBundleIdentifier
- #define JucePlugin_CFBundleIdentifier com.JDI.JaySynth
-#endif
-#ifndef JucePlugin_AUCocoaViewClassName
- #define JucePlugin_AUCocoaViewClassName JaySynthAU_V1
+ #define JucePlugin_CFBundleIdentifier com.yourcompany.JaySynth
#endif
#ifndef JucePlugin_RTASCategory
#define JucePlugin_RTASCategory ePlugInCategory_SWGenerators
@@ -255,5 +277,29 @@
#ifndef JucePlugin_RTASProductId
#define JucePlugin_RTASProductId JucePlugin_PluginCode
#endif
+#ifndef JucePlugin_RTASDisableBypass
+ #define JucePlugin_RTASDisableBypass 0
+#endif
+#ifndef JucePlugin_RTASDisableMultiMono
+ #define JucePlugin_RTASDisableMultiMono 0
+#endif
+#ifndef JucePlugin_AAXIdentifier
+ #define JucePlugin_AAXIdentifier com.yourcompany.JaySynth
+#endif
+#ifndef JucePlugin_AAXManufacturerCode
+ #define JucePlugin_AAXManufacturerCode JucePlugin_ManufacturerCode
+#endif
+#ifndef JucePlugin_AAXProductId
+ #define JucePlugin_AAXProductId JucePlugin_PluginCode
+#endif
+#ifndef JucePlugin_AAXCategory
+ #define JucePlugin_AAXCategory AAX_ePlugInCategory_Dynamics
+#endif
+#ifndef JucePlugin_AAXDisableBypass
+ #define JucePlugin_AAXDisableBypass 0
+#endif
+#ifndef JucePlugin_AAXDisableMultiMono
+ #define JucePlugin_AAXDisableMultiMono 0
+#endif
-#endif // __JUCE_APPCONFIG_SGFP29__
+#endif // __JUCE_APPCONFIG_FGWBAQ__
diff --git a/JuceLibraryCode/JuceHeader.h b/JuceLibraryCode/JuceHeader.h
index cc8c27e..239810d 100644
--- a/JuceLibraryCode/JuceHeader.h
+++ b/JuceLibraryCode/JuceHeader.h
@@ -10,11 +10,10 @@
*/
-#ifndef __APPHEADERFILE_SGFP29__
-#define __APPHEADERFILE_SGFP29__
+#ifndef __APPHEADERFILE_FGWBAQ__
+#define __APPHEADERFILE_FGWBAQ__
#include "AppConfig.h"
-
#include "modules/juce_audio_basics/juce_audio_basics.h"
#include "modules/juce_audio_devices/juce_audio_devices.h"
#include "modules/juce_audio_formats/juce_audio_formats.h"
@@ -35,9 +34,9 @@
namespace ProjectInfo
{
- const char* const projectName = JucePlugin_Name;
- const char* const versionString = JucePlugin_VersionString;
- const int versionNumber = JucePlugin_VersionCode;
+ const char* const projectName = "JaySynth";
+ const char* const versionString = "1.0.0";
+ const int versionNumber = 0x10000;
}
-#endif // __APPHEADERFILE_SGFP29__
+#endif // __APPHEADERFILE_FGWBAQ__
diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp
new file mode 100644
index 0000000..a78b395
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp
@@ -0,0 +1,600 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
+{
+ const double maxVal = (double) 0x7fff;
+ char* intData = static_cast (dest);
+
+ if (dest != (void*) source || destBytesPerSample <= 4)
+ {
+ for (int i = 0; i < numSamples; ++i)
+ {
+ *(uint16*) intData = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
+ intData += destBytesPerSample;
+ }
+ }
+ else
+ {
+ intData += destBytesPerSample * numSamples;
+
+ for (int i = numSamples; --i >= 0;)
+ {
+ intData -= destBytesPerSample;
+ *(uint16*) intData = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
+ }
+ }
+}
+
+void AudioDataConverters::convertFloatToInt16BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
+{
+ const double maxVal = (double) 0x7fff;
+ char* intData = static_cast (dest);
+
+ if (dest != (void*) source || destBytesPerSample <= 4)
+ {
+ for (int i = 0; i < numSamples; ++i)
+ {
+ *(uint16*) intData = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
+ intData += destBytesPerSample;
+ }
+ }
+ else
+ {
+ intData += destBytesPerSample * numSamples;
+
+ for (int i = numSamples; --i >= 0;)
+ {
+ intData -= destBytesPerSample;
+ *(uint16*) intData = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
+ }
+ }
+}
+
+void AudioDataConverters::convertFloatToInt24LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
+{
+ const double maxVal = (double) 0x7fffff;
+ char* intData = static_cast (dest);
+
+ if (dest != (void*) source || destBytesPerSample <= 4)
+ {
+ for (int i = 0; i < numSamples; ++i)
+ {
+ ByteOrder::littleEndian24BitToChars (roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData);
+ intData += destBytesPerSample;
+ }
+ }
+ else
+ {
+ intData += destBytesPerSample * numSamples;
+
+ for (int i = numSamples; --i >= 0;)
+ {
+ intData -= destBytesPerSample;
+ ByteOrder::littleEndian24BitToChars (roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData);
+ }
+ }
+}
+
+void AudioDataConverters::convertFloatToInt24BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
+{
+ const double maxVal = (double) 0x7fffff;
+ char* intData = static_cast (dest);
+
+ if (dest != (void*) source || destBytesPerSample <= 4)
+ {
+ for (int i = 0; i < numSamples; ++i)
+ {
+ ByteOrder::bigEndian24BitToChars (roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData);
+ intData += destBytesPerSample;
+ }
+ }
+ else
+ {
+ intData += destBytesPerSample * numSamples;
+
+ for (int i = numSamples; --i >= 0;)
+ {
+ intData -= destBytesPerSample;
+ ByteOrder::bigEndian24BitToChars (roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData);
+ }
+ }
+}
+
+void AudioDataConverters::convertFloatToInt32LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
+{
+ const double maxVal = (double) 0x7fffffff;
+ char* intData = static_cast (dest);
+
+ if (dest != (void*) source || destBytesPerSample <= 4)
+ {
+ for (int i = 0; i < numSamples; ++i)
+ {
+ *(uint32*)intData = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
+ intData += destBytesPerSample;
+ }
+ }
+ else
+ {
+ intData += destBytesPerSample * numSamples;
+
+ for (int i = numSamples; --i >= 0;)
+ {
+ intData -= destBytesPerSample;
+ *(uint32*)intData = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
+ }
+ }
+}
+
+void AudioDataConverters::convertFloatToInt32BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
+{
+ const double maxVal = (double) 0x7fffffff;
+ char* intData = static_cast (dest);
+
+ if (dest != (void*) source || destBytesPerSample <= 4)
+ {
+ for (int i = 0; i < numSamples; ++i)
+ {
+ *(uint32*)intData = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
+ intData += destBytesPerSample;
+ }
+ }
+ else
+ {
+ intData += destBytesPerSample * numSamples;
+
+ for (int i = numSamples; --i >= 0;)
+ {
+ intData -= destBytesPerSample;
+ *(uint32*)intData = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
+ }
+ }
+}
+
+void AudioDataConverters::convertFloatToFloat32LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
+{
+ jassert (dest != (void*) source || destBytesPerSample <= 4); // This op can't be performed on in-place data!
+
+ char* d = static_cast (dest);
+
+ for (int i = 0; i < numSamples; ++i)
+ {
+ *(float*) d = source[i];
+
+ #if JUCE_BIG_ENDIAN
+ *(uint32*) d = ByteOrder::swap (*(uint32*) d);
+ #endif
+
+ d += destBytesPerSample;
+ }
+}
+
+void AudioDataConverters::convertFloatToFloat32BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
+{
+ jassert (dest != (void*) source || destBytesPerSample <= 4); // This op can't be performed on in-place data!
+
+ char* d = static_cast (dest);
+
+ for (int i = 0; i < numSamples; ++i)
+ {
+ *(float*) d = source[i];
+
+ #if JUCE_LITTLE_ENDIAN
+ *(uint32*) d = ByteOrder::swap (*(uint32*) d);
+ #endif
+
+ d += destBytesPerSample;
+ }
+}
+
+//==============================================================================
+void AudioDataConverters::convertInt16LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
+{
+ const float scale = 1.0f / 0x7fff;
+ const char* intData = static_cast (source);
+
+ if (source != (void*) dest || srcBytesPerSample >= 4)
+ {
+ for (int i = 0; i < numSamples; ++i)
+ {
+ dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*(uint16*)intData);
+ intData += srcBytesPerSample;
+ }
+ }
+ else
+ {
+ intData += srcBytesPerSample * numSamples;
+
+ for (int i = numSamples; --i >= 0;)
+ {
+ intData -= srcBytesPerSample;
+ dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*(uint16*)intData);
+ }
+ }
+}
+
+void AudioDataConverters::convertInt16BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
+{
+ const float scale = 1.0f / 0x7fff;
+ const char* intData = static_cast (source);
+
+ if (source != (void*) dest || srcBytesPerSample >= 4)
+ {
+ for (int i = 0; i < numSamples; ++i)
+ {
+ dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*(uint16*)intData);
+ intData += srcBytesPerSample;
+ }
+ }
+ else
+ {
+ intData += srcBytesPerSample * numSamples;
+
+ for (int i = numSamples; --i >= 0;)
+ {
+ intData -= srcBytesPerSample;
+ dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*(uint16*)intData);
+ }
+ }
+}
+
+void AudioDataConverters::convertInt24LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
+{
+ const float scale = 1.0f / 0x7fffff;
+ const char* intData = static_cast (source);
+
+ if (source != (void*) dest || srcBytesPerSample >= 4)
+ {
+ for (int i = 0; i < numSamples; ++i)
+ {
+ dest[i] = scale * (short) ByteOrder::littleEndian24Bit (intData);
+ intData += srcBytesPerSample;
+ }
+ }
+ else
+ {
+ intData += srcBytesPerSample * numSamples;
+
+ for (int i = numSamples; --i >= 0;)
+ {
+ intData -= srcBytesPerSample;
+ dest[i] = scale * (short) ByteOrder::littleEndian24Bit (intData);
+ }
+ }
+}
+
+void AudioDataConverters::convertInt24BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
+{
+ const float scale = 1.0f / 0x7fffff;
+ const char* intData = static_cast (source);
+
+ if (source != (void*) dest || srcBytesPerSample >= 4)
+ {
+ for (int i = 0; i < numSamples; ++i)
+ {
+ dest[i] = scale * (short) ByteOrder::bigEndian24Bit (intData);
+ intData += srcBytesPerSample;
+ }
+ }
+ else
+ {
+ intData += srcBytesPerSample * numSamples;
+
+ for (int i = numSamples; --i >= 0;)
+ {
+ intData -= srcBytesPerSample;
+ dest[i] = scale * (short) ByteOrder::bigEndian24Bit (intData);
+ }
+ }
+}
+
+void AudioDataConverters::convertInt32LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
+{
+ const float scale = 1.0f / 0x7fffffff;
+ const char* intData = static_cast (source);
+
+ if (source != (void*) dest || srcBytesPerSample >= 4)
+ {
+ for (int i = 0; i < numSamples; ++i)
+ {
+ dest[i] = scale * (int) ByteOrder::swapIfBigEndian (*(uint32*) intData);
+ intData += srcBytesPerSample;
+ }
+ }
+ else
+ {
+ intData += srcBytesPerSample * numSamples;
+
+ for (int i = numSamples; --i >= 0;)
+ {
+ intData -= srcBytesPerSample;
+ dest[i] = scale * (int) ByteOrder::swapIfBigEndian (*(uint32*) intData);
+ }
+ }
+}
+
+void AudioDataConverters::convertInt32BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
+{
+ const float scale = 1.0f / 0x7fffffff;
+ const char* intData = static_cast (source);
+
+ if (source != (void*) dest || srcBytesPerSample >= 4)
+ {
+ for (int i = 0; i < numSamples; ++i)
+ {
+ dest[i] = scale * (int) ByteOrder::swapIfLittleEndian (*(uint32*) intData);
+ intData += srcBytesPerSample;
+ }
+ }
+ else
+ {
+ intData += srcBytesPerSample * numSamples;
+
+ for (int i = numSamples; --i >= 0;)
+ {
+ intData -= srcBytesPerSample;
+ dest[i] = scale * (int) ByteOrder::swapIfLittleEndian (*(uint32*) intData);
+ }
+ }
+}
+
+void AudioDataConverters::convertFloat32LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
+{
+ const char* s = static_cast (source);
+
+ for (int i = 0; i < numSamples; ++i)
+ {
+ dest[i] = *(float*)s;
+
+ #if JUCE_BIG_ENDIAN
+ uint32* const d = (uint32*) (dest + i);
+ *d = ByteOrder::swap (*d);
+ #endif
+
+ s += srcBytesPerSample;
+ }
+}
+
+void AudioDataConverters::convertFloat32BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
+{
+ const char* s = static_cast (source);
+
+ for (int i = 0; i < numSamples; ++i)
+ {
+ dest[i] = *(float*)s;
+
+ #if JUCE_LITTLE_ENDIAN
+ uint32* const d = (uint32*) (dest + i);
+ *d = ByteOrder::swap (*d);
+ #endif
+
+ s += srcBytesPerSample;
+ }
+}
+
+
+//==============================================================================
+void AudioDataConverters::convertFloatToFormat (const DataFormat destFormat,
+ const float* const source,
+ void* const dest,
+ const int numSamples)
+{
+ switch (destFormat)
+ {
+ case int16LE: convertFloatToInt16LE (source, dest, numSamples); break;
+ case int16BE: convertFloatToInt16BE (source, dest, numSamples); break;
+ case int24LE: convertFloatToInt24LE (source, dest, numSamples); break;
+ case int24BE: convertFloatToInt24BE (source, dest, numSamples); break;
+ case int32LE: convertFloatToInt32LE (source, dest, numSamples); break;
+ case int32BE: convertFloatToInt32BE (source, dest, numSamples); break;
+ case float32LE: convertFloatToFloat32LE (source, dest, numSamples); break;
+ case float32BE: convertFloatToFloat32BE (source, dest, numSamples); break;
+ default: jassertfalse; break;
+ }
+}
+
+void AudioDataConverters::convertFormatToFloat (const DataFormat sourceFormat,
+ const void* const source,
+ float* const dest,
+ const int numSamples)
+{
+ switch (sourceFormat)
+ {
+ case int16LE: convertInt16LEToFloat (source, dest, numSamples); break;
+ case int16BE: convertInt16BEToFloat (source, dest, numSamples); break;
+ case int24LE: convertInt24LEToFloat (source, dest, numSamples); break;
+ case int24BE: convertInt24BEToFloat (source, dest, numSamples); break;
+ case int32LE: convertInt32LEToFloat (source, dest, numSamples); break;
+ case int32BE: convertInt32BEToFloat (source, dest, numSamples); break;
+ case float32LE: convertFloat32LEToFloat (source, dest, numSamples); break;
+ case float32BE: convertFloat32BEToFloat (source, dest, numSamples); break;
+ default: jassertfalse; break;
+ }
+}
+
+//==============================================================================
+void AudioDataConverters::interleaveSamples (const float** const source,
+ float* const dest,
+ const int numSamples,
+ const int numChannels)
+{
+ for (int chan = 0; chan < numChannels; ++chan)
+ {
+ int i = chan;
+ const float* src = source [chan];
+
+ for (int j = 0; j < numSamples; ++j)
+ {
+ dest [i] = src [j];
+ i += numChannels;
+ }
+ }
+}
+
+void AudioDataConverters::deinterleaveSamples (const float* const source,
+ float** const dest,
+ const int numSamples,
+ const int numChannels)
+{
+ for (int chan = 0; chan < numChannels; ++chan)
+ {
+ int i = chan;
+ float* dst = dest [chan];
+
+ for (int j = 0; j < numSamples; ++j)
+ {
+ dst [j] = source [i];
+ i += numChannels;
+ }
+ }
+}
+
+
+//==============================================================================
+#if JUCE_UNIT_TESTS
+
+class AudioConversionTests : public UnitTest
+{
+public:
+ AudioConversionTests() : UnitTest ("Audio data conversion") {}
+
+ template
+ struct Test5
+ {
+ static void test (UnitTest& unitTest, Random& r)
+ {
+ test (unitTest, false, r);
+ test (unitTest, true, r);
+ }
+
+ static void test (UnitTest& unitTest, bool inPlace, Random& r)
+ {
+ const int numSamples = 2048;
+ int32 original [numSamples], converted [numSamples], reversed [numSamples];
+
+ {
+ AudioData::Pointer d (original);
+ bool clippingFailed = false;
+
+ for (int i = 0; i < numSamples / 2; ++i)
+ {
+ d.setAsFloat (r.nextFloat() * 2.2f - 1.1f);
+
+ if (! d.isFloatingPoint())
+ clippingFailed = d.getAsFloat() > 1.0f || d.getAsFloat() < -1.0f || clippingFailed;
+
+ ++d;
+ d.setAsInt32 (r.nextInt());
+ ++d;
+ }
+
+ unitTest.expect (! clippingFailed);
+ }
+
+ // convert data from the source to dest format..
+ ScopedPointer conv (new AudioData::ConverterInstance ,
+ AudioData::Pointer >());
+ conv->convertSamples (inPlace ? reversed : converted, original, numSamples);
+
+ // ..and back again..
+ conv = new AudioData::ConverterInstance ,
+ AudioData::Pointer >();
+ if (! inPlace)
+ zeromem (reversed, sizeof (reversed));
+
+ conv->convertSamples (reversed, inPlace ? reversed : converted, numSamples);
+
+ {
+ int biggestDiff = 0;
+ AudioData::Pointer d1 (original);
+ AudioData::Pointer d2 (reversed);
+
+ const int errorMargin = 2 * AudioData::Pointer::get32BitResolution()
+ + AudioData::Pointer::get32BitResolution();
+
+ for (int i = 0; i < numSamples; ++i)
+ {
+ biggestDiff = jmax (biggestDiff, std::abs (d1.getAsInt32() - d2.getAsInt32()));
+ ++d1;
+ ++d2;
+ }
+
+ unitTest.expect (biggestDiff <= errorMargin);
+ }
+ }
+ };
+
+ template
+ struct Test3
+ {
+ static void test (UnitTest& unitTest, Random& r)
+ {
+ Test5 ::test (unitTest, r);
+ Test5 ::test (unitTest, r);
+ }
+ };
+
+ template
+ struct Test2
+ {
+ static void test (UnitTest& unitTest, Random& r)
+ {
+ Test3 ::test (unitTest, r);
+ Test3 ::test (unitTest, r);
+ Test3 ::test (unitTest, r);
+ Test3 ::test (unitTest, r);
+ Test3 ::test (unitTest, r);
+ Test3 ::test (unitTest, r);
+ }
+ };
+
+ template
+ struct Test1
+ {
+ static void test (UnitTest& unitTest, Random& r)
+ {
+ Test2 ::test (unitTest, r);
+ Test2 ::test (unitTest, r);
+ }
+ };
+
+ void runTest()
+ {
+ Random r = getRandom();
+ beginTest ("Round-trip conversion: Int8");
+ Test1 ::test (*this, r);
+ beginTest ("Round-trip conversion: Int16");
+ Test1 ::test (*this, r);
+ beginTest ("Round-trip conversion: Int24");
+ Test1 ::test (*this, r);
+ beginTest ("Round-trip conversion: Int32");
+ Test1 ::test (*this, r);
+ beginTest ("Round-trip conversion: Float32");
+ Test1 ::test (*this, r);
+ }
+};
+
+static AudioConversionTests audioConversionUnitTests;
+
+#endif
diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h
new file mode 100644
index 0000000..b3f49a6
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h
@@ -0,0 +1,710 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_AUDIODATACONVERTERS_H_INCLUDED
+#define JUCE_AUDIODATACONVERTERS_H_INCLUDED
+
+
+//==============================================================================
+/**
+ This class a container which holds all the classes pertaining to the AudioData::Pointer
+ audio sample format class.
+
+ @see AudioData::Pointer.
+*/
+class JUCE_API AudioData
+{
+public:
+ //==============================================================================
+ // These types can be used as the SampleFormat template parameter for the AudioData::Pointer class.
+
+ class Int8; /**< Used as a template parameter for AudioData::Pointer. Indicates an 8-bit integer packed data format. */
+ class UInt8; /**< Used as a template parameter for AudioData::Pointer. Indicates an 8-bit unsigned integer packed data format. */
+ class Int16; /**< Used as a template parameter for AudioData::Pointer. Indicates an 16-bit integer packed data format. */
+ class Int24; /**< Used as a template parameter for AudioData::Pointer. Indicates an 24-bit integer packed data format. */
+ class Int32; /**< Used as a template parameter for AudioData::Pointer. Indicates an 32-bit integer packed data format. */
+ class Float32; /**< Used as a template parameter for AudioData::Pointer. Indicates an 32-bit float data format. */
+
+ //==============================================================================
+ // These types can be used as the Endianness template parameter for the AudioData::Pointer class.
+
+ class BigEndian; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are stored in big-endian order. */
+ class LittleEndian; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are stored in little-endian order. */
+ class NativeEndian; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are stored in the CPU's native endianness. */
+
+ //==============================================================================
+ // These types can be used as the InterleavingType template parameter for the AudioData::Pointer class.
+
+ class NonInterleaved; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are stored contiguously. */
+ class Interleaved; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are interleaved with a number of other channels. */
+
+ //==============================================================================
+ // These types can be used as the Constness template parameter for the AudioData::Pointer class.
+
+ class NonConst; /**< Used as a template parameter for AudioData::Pointer. Indicates that the pointer can be used for non-const data. */
+ class Const; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples can only be used for const data.. */
+
+ #ifndef DOXYGEN
+ //==============================================================================
+ class BigEndian
+ {
+ public:
+ template static inline float getAsFloat (SampleFormatType& s) noexcept { return s.getAsFloatBE(); }
+ template static inline void setAsFloat (SampleFormatType& s, float newValue) noexcept { s.setAsFloatBE (newValue); }
+ template static inline int32 getAsInt32 (SampleFormatType& s) noexcept { return s.getAsInt32BE(); }
+ template static inline void setAsInt32 (SampleFormatType& s, int32 newValue) noexcept { s.setAsInt32BE (newValue); }
+ template static inline void copyFrom (DestType& dest, SourceType& source) noexcept { dest.copyFromBE (source); }
+ enum { isBigEndian = 1 };
+ };
+
+ class LittleEndian
+ {
+ public:
+ template static inline float getAsFloat (SampleFormatType& s) noexcept { return s.getAsFloatLE(); }
+ template static inline void setAsFloat (SampleFormatType& s, float newValue) noexcept { s.setAsFloatLE (newValue); }
+ template static inline int32 getAsInt32 (SampleFormatType& s) noexcept { return s.getAsInt32LE(); }
+ template static inline void setAsInt32 (SampleFormatType& s, int32 newValue) noexcept { s.setAsInt32LE (newValue); }
+ template static inline void copyFrom (DestType& dest, SourceType& source) noexcept { dest.copyFromLE (source); }
+ enum { isBigEndian = 0 };
+ };
+
+ #if JUCE_BIG_ENDIAN
+ class NativeEndian : public BigEndian {};
+ #else
+ class NativeEndian : public LittleEndian {};
+ #endif
+
+ //==============================================================================
+ class Int8
+ {
+ public:
+ inline Int8 (void* d) noexcept : data (static_cast (d)) {}
+
+ inline void advance() noexcept { ++data; }
+ inline void skip (int numSamples) noexcept { data += numSamples; }
+ inline float getAsFloatLE() const noexcept { return (float) (*data * (1.0 / (1.0 + maxValue))); }
+ inline float getAsFloatBE() const noexcept { return getAsFloatLE(); }
+ inline void setAsFloatLE (float newValue) noexcept { *data = (int8) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue))); }
+ inline void setAsFloatBE (float newValue) noexcept { setAsFloatLE (newValue); }
+ inline int32 getAsInt32LE() const noexcept { return (int) (*data << 24); }
+ inline int32 getAsInt32BE() const noexcept { return getAsInt32LE(); }
+ inline void setAsInt32LE (int newValue) noexcept { *data = (int8) (newValue >> 24); }
+ inline void setAsInt32BE (int newValue) noexcept { setAsInt32LE (newValue); }
+ inline void clear() noexcept { *data = 0; }
+ inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;}
+ template inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); }
+ template inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); }
+ inline void copyFromSameType (Int8& source) noexcept { *data = *source.data; }
+
+ int8* data;
+ enum { bytesPerSample = 1, maxValue = 0x7f, resolution = (1 << 24), isFloat = 0 };
+ };
+
+ class UInt8
+ {
+ public:
+ inline UInt8 (void* d) noexcept : data (static_cast (d)) {}
+
+ inline void advance() noexcept { ++data; }
+ inline void skip (int numSamples) noexcept { data += numSamples; }
+ inline float getAsFloatLE() const noexcept { return (float) ((*data - 128) * (1.0 / (1.0 + maxValue))); }
+ inline float getAsFloatBE() const noexcept { return getAsFloatLE(); }
+ inline void setAsFloatLE (float newValue) noexcept { *data = (uint8) jlimit (0, 255, 128 + roundToInt (newValue * (1.0 + maxValue))); }
+ inline void setAsFloatBE (float newValue) noexcept { setAsFloatLE (newValue); }
+ inline int32 getAsInt32LE() const noexcept { return (int) ((*data - 128) << 24); }
+ inline int32 getAsInt32BE() const noexcept { return getAsInt32LE(); }
+ inline void setAsInt32LE (int newValue) noexcept { *data = (uint8) (128 + (newValue >> 24)); }
+ inline void setAsInt32BE (int newValue) noexcept { setAsInt32LE (newValue); }
+ inline void clear() noexcept { *data = 128; }
+ inline void clearMultiple (int num) noexcept { memset (data, 128, (size_t) num) ;}
+ template inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); }
+ template inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); }
+ inline void copyFromSameType (UInt8& source) noexcept { *data = *source.data; }
+
+ uint8* data;
+ enum { bytesPerSample = 1, maxValue = 0x7f, resolution = (1 << 24), isFloat = 0 };
+ };
+
+ class Int16
+ {
+ public:
+ inline Int16 (void* d) noexcept : data (static_cast (d)) {}
+
+ inline void advance() noexcept { ++data; }
+ inline void skip (int numSamples) noexcept { data += numSamples; }
+ inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int16) ByteOrder::swapIfBigEndian (*data)); }
+ inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int16) ByteOrder::swapIfLittleEndian (*data)); }
+ inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint16) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue)))); }
+ inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint16) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue)))); }
+ inline int32 getAsInt32LE() const noexcept { return (int32) (ByteOrder::swapIfBigEndian ((uint16) *data) << 16); }
+ inline int32 getAsInt32BE() const noexcept { return (int32) (ByteOrder::swapIfLittleEndian ((uint16) *data) << 16); }
+ inline void setAsInt32LE (int32 newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint16) (newValue >> 16)); }
+ inline void setAsInt32BE (int32 newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint16) (newValue >> 16)); }
+ inline void clear() noexcept { *data = 0; }
+ inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;}
+ template inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); }
+ template inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); }
+ inline void copyFromSameType (Int16& source) noexcept { *data = *source.data; }
+
+ uint16* data;
+ enum { bytesPerSample = 2, maxValue = 0x7fff, resolution = (1 << 16), isFloat = 0 };
+ };
+
+ class Int24
+ {
+ public:
+ inline Int24 (void* d) noexcept : data (static_cast (d)) {}
+
+ inline void advance() noexcept { data += 3; }
+ inline void skip (int numSamples) noexcept { data += 3 * numSamples; }
+ inline float getAsFloatLE() const noexcept { return (float) (ByteOrder::littleEndian24Bit (data) * (1.0 / (1.0 + maxValue))); }
+ inline float getAsFloatBE() const noexcept { return (float) (ByteOrder::bigEndian24Bit (data) * (1.0 / (1.0 + maxValue))); }
+ inline void setAsFloatLE (float newValue) noexcept { ByteOrder::littleEndian24BitToChars (jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue))), data); }
+ inline void setAsFloatBE (float newValue) noexcept { ByteOrder::bigEndian24BitToChars (jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue))), data); }
+ inline int32 getAsInt32LE() const noexcept { return (int32) ByteOrder::littleEndian24Bit (data) << 8; }
+ inline int32 getAsInt32BE() const noexcept { return (int32) ByteOrder::bigEndian24Bit (data) << 8; }
+ inline void setAsInt32LE (int32 newValue) noexcept { ByteOrder::littleEndian24BitToChars (newValue >> 8, data); }
+ inline void setAsInt32BE (int32 newValue) noexcept { ByteOrder::bigEndian24BitToChars (newValue >> 8, data); }
+ inline void clear() noexcept { data[0] = 0; data[1] = 0; data[2] = 0; }
+ inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;}
+ template inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); }
+ template inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); }
+ inline void copyFromSameType (Int24& source) noexcept { data[0] = source.data[0]; data[1] = source.data[1]; data[2] = source.data[2]; }
+
+ char* data;
+ enum { bytesPerSample = 3, maxValue = 0x7fffff, resolution = (1 << 8), isFloat = 0 };
+ };
+
+ class Int32
+ {
+ public:
+ inline Int32 (void* d) noexcept : data (static_cast (d)) {}
+
+ inline void advance() noexcept { ++data; }
+ inline void skip (int numSamples) noexcept { data += numSamples; }
+ inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfBigEndian (*data)); }
+ inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfLittleEndian (*data)); }
+ inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); }
+ inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); }
+ inline int32 getAsInt32LE() const noexcept { return (int32) ByteOrder::swapIfBigEndian (*data); }
+ inline int32 getAsInt32BE() const noexcept { return (int32) ByteOrder::swapIfLittleEndian (*data); }
+ inline void setAsInt32LE (int32 newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) newValue); }
+ inline void setAsInt32BE (int32 newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) newValue); }
+ inline void clear() noexcept { *data = 0; }
+ inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;}
+ template inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); }
+ template inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); }
+ inline void copyFromSameType (Int32& source) noexcept { *data = *source.data; }
+
+ uint32* data;
+ enum { bytesPerSample = 4, maxValue = 0x7fffffff, resolution = 1, isFloat = 0 };
+ };
+
+ /** A 32-bit integer type, of which only the bottom 24 bits are used. */
+ class Int24in32 : public Int32
+ {
+ public:
+ inline Int24in32 (void* d) noexcept : Int32 (d) {}
+
+ inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfBigEndian (*data)); }
+ inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfLittleEndian (*data)); }
+ inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); }
+ inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); }
+ inline int32 getAsInt32LE() const noexcept { return (int32) ByteOrder::swapIfBigEndian (*data) << 8; }
+ inline int32 getAsInt32BE() const noexcept { return (int32) ByteOrder::swapIfLittleEndian (*data) << 8; }
+ inline void setAsInt32LE (int32 newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) newValue >> 8); }
+ inline void setAsInt32BE (int32 newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) newValue >> 8); }
+ template inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); }
+ template inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); }
+ inline void copyFromSameType (Int24in32& source) noexcept { *data = *source.data; }
+
+ enum { bytesPerSample = 4, maxValue = 0x7fffff, resolution = (1 << 8), isFloat = 0 };
+ };
+
+ class Float32
+ {
+ public:
+ inline Float32 (void* d) noexcept : data (static_cast (d)) {}
+
+ inline void advance() noexcept { ++data; }
+ inline void skip (int numSamples) noexcept { data += numSamples; }
+ #if JUCE_BIG_ENDIAN
+ inline float getAsFloatBE() const noexcept { return *data; }
+ inline void setAsFloatBE (float newValue) noexcept { *data = newValue; }
+ inline float getAsFloatLE() const noexcept { union { uint32 asInt; float asFloat; } n; n.asInt = ByteOrder::swap (*(uint32*) data); return n.asFloat; }
+ inline void setAsFloatLE (float newValue) noexcept { union { uint32 asInt; float asFloat; } n; n.asFloat = newValue; *(uint32*) data = ByteOrder::swap (n.asInt); }
+ #else
+ inline float getAsFloatLE() const noexcept { return *data; }
+ inline void setAsFloatLE (float newValue) noexcept { *data = newValue; }
+ inline float getAsFloatBE() const noexcept { union { uint32 asInt; float asFloat; } n; n.asInt = ByteOrder::swap (*(uint32*) data); return n.asFloat; }
+ inline void setAsFloatBE (float newValue) noexcept { union { uint32 asInt; float asFloat; } n; n.asFloat = newValue; *(uint32*) data = ByteOrder::swap (n.asInt); }
+ #endif
+ inline int32 getAsInt32LE() const noexcept { return (int32) roundToInt (jlimit (-1.0, 1.0, (double) getAsFloatLE()) * (double) maxValue); }
+ inline int32 getAsInt32BE() const noexcept { return (int32) roundToInt (jlimit (-1.0, 1.0, (double) getAsFloatBE()) * (double) maxValue); }
+ inline void setAsInt32LE (int32 newValue) noexcept { setAsFloatLE ((float) (newValue * (1.0 / (1.0 + maxValue)))); }
+ inline void setAsInt32BE (int32 newValue) noexcept { setAsFloatBE ((float) (newValue * (1.0 / (1.0 + maxValue)))); }
+ inline void clear() noexcept { *data = 0; }
+ inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;}
+ template inline void copyFromLE (SourceType& source) noexcept { setAsFloatLE (source.getAsFloat()); }
+ template inline void copyFromBE (SourceType& source) noexcept { setAsFloatBE (source.getAsFloat()); }
+ inline void copyFromSameType (Float32& source) noexcept { *data = *source.data; }
+
+ float* data;
+ enum { bytesPerSample = 4, maxValue = 0x7fffffff, resolution = (1 << 8), isFloat = 1 };
+ };
+
+ //==============================================================================
+ class NonInterleaved
+ {
+ public:
+ inline NonInterleaved() noexcept {}
+ inline NonInterleaved (const NonInterleaved&) noexcept {}
+ inline NonInterleaved (const int) noexcept {}
+ inline void copyFrom (const NonInterleaved&) noexcept {}
+ template inline void advanceData (SampleFormatType& s) noexcept { s.advance(); }
+ template inline void advanceDataBy (SampleFormatType& s, int numSamples) noexcept { s.skip (numSamples); }
+ template inline void clear (SampleFormatType& s, int numSamples) noexcept { s.clearMultiple (numSamples); }
+ template inline static int getNumBytesBetweenSamples (const SampleFormatType&) noexcept { return SampleFormatType::bytesPerSample; }
+
+ enum { isInterleavedType = 0, numInterleavedChannels = 1 };
+ };
+
+ class Interleaved
+ {
+ public:
+ inline Interleaved() noexcept : numInterleavedChannels (1) {}
+ inline Interleaved (const Interleaved& other) noexcept : numInterleavedChannels (other.numInterleavedChannels) {}
+ inline Interleaved (const int numInterleavedChans) noexcept : numInterleavedChannels (numInterleavedChans) {}
+ inline void copyFrom (const Interleaved& other) noexcept { numInterleavedChannels = other.numInterleavedChannels; }
+ template inline void advanceData (SampleFormatType& s) noexcept { s.skip (numInterleavedChannels); }
+ template inline void advanceDataBy (SampleFormatType& s, int numSamples) noexcept { s.skip (numInterleavedChannels * numSamples); }
+ template inline void clear (SampleFormatType& s, int numSamples) noexcept { while (--numSamples >= 0) { s.clear(); s.skip (numInterleavedChannels); } }
+ template inline int getNumBytesBetweenSamples (const SampleFormatType&) const noexcept { return numInterleavedChannels * SampleFormatType::bytesPerSample; }
+ int numInterleavedChannels;
+ enum { isInterleavedType = 1 };
+ };
+
+ //==============================================================================
+ class NonConst
+ {
+ public:
+ typedef void VoidType;
+ static inline void* toVoidPtr (VoidType* v) noexcept { return v; }
+ enum { isConst = 0 };
+ };
+
+ class Const
+ {
+ public:
+ typedef const void VoidType;
+ static inline void* toVoidPtr (VoidType* v) noexcept { return const_cast (v); }
+ enum { isConst = 1 };
+ };
+ #endif
+
+ //==============================================================================
+ /**
+ A pointer to a block of audio data with a particular encoding.
+
+ This object can be used to read and write from blocks of encoded audio samples. To create one, you specify
+ the audio format as a series of template parameters, e.g.
+ @code
+ // this creates a pointer for reading from a const array of 16-bit little-endian packed samples.
+ AudioData::Pointer pointer (someRawAudioData);
+
+ // These methods read the sample that is being pointed to
+ float firstSampleAsFloat = pointer.getAsFloat();
+ int32 firstSampleAsInt = pointer.getAsInt32();
+ ++pointer; // moves the pointer to the next sample.
+ pointer += 3; // skips the next 3 samples.
+ @endcode
+
+ The convertSamples() method lets you copy a range of samples from one format to another, automatically
+ converting its format.
+
+ @see AudioData::Converter
+ */
+ template
+ class Pointer : private InterleavingType // (inherited for EBCO)
+ {
+ public:
+ //==============================================================================
+ /** Creates a non-interleaved pointer from some raw data in the appropriate format.
+ This constructor is only used if you've specified the AudioData::NonInterleaved option -
+ for interleaved formats, use the constructor that also takes a number of channels.
+ */
+ Pointer (typename Constness::VoidType* sourceData) noexcept
+ : data (Constness::toVoidPtr (sourceData))
+ {
+ // If you're using interleaved data, call the other constructor! If you're using non-interleaved data,
+ // you should pass NonInterleaved as the template parameter for the interleaving type!
+ static_jassert (InterleavingType::isInterleavedType == 0);
+ }
+
+ /** Creates a pointer from some raw data in the appropriate format with the specified number of interleaved channels.
+ For non-interleaved data, use the other constructor.
+ */
+ Pointer (typename Constness::VoidType* sourceData, int numInterleaved) noexcept
+ : InterleavingType (numInterleaved), data (Constness::toVoidPtr (sourceData))
+ {
+ }
+
+ /** Creates a copy of another pointer. */
+ Pointer (const Pointer& other) noexcept
+ : InterleavingType (other), data (other.data)
+ {
+ }
+
+ Pointer& operator= (const Pointer& other) noexcept
+ {
+ InterleavingType::operator= (other);
+ data = other.data;
+ return *this;
+ }
+
+ //==============================================================================
+ /** Returns the value of the first sample as a floating point value.
+ The value will be in the range -1.0 to 1.0 for integer formats. For floating point
+ formats, the value could be outside that range, although -1 to 1 is the standard range.
+ */
+ inline float getAsFloat() const noexcept { return Endianness::getAsFloat (data); }
+
+ /** Sets the value of the first sample as a floating point value.
+
+ (This method can only be used if the AudioData::NonConst option was used).
+ The value should be in the range -1.0 to 1.0 - for integer formats, values outside that
+ range will be clipped. For floating point formats, any value passed in here will be
+ written directly, although -1 to 1 is the standard range.
+ */
+ inline void setAsFloat (float newValue) noexcept
+ {
+ static_jassert (Constness::isConst == 0); // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead!
+ Endianness::setAsFloat (data, newValue);
+ }
+
+ /** Returns the value of the first sample as a 32-bit integer.
+ The value returned will be in the range 0x80000000 to 0x7fffffff, and shorter values will be
+ shifted to fill this range (e.g. if you're reading from 24-bit data, the values will be shifted up
+ by 8 bits when returned here). If the source data is floating point, values beyond -1.0 to 1.0 will
+ be clipped so that -1.0 maps onto -0x7fffffff and 1.0 maps to 0x7fffffff.
+ */
+ inline int32 getAsInt32() const noexcept { return Endianness::getAsInt32 (data); }
+
+ /** Sets the value of the first sample as a 32-bit integer.
+ This will be mapped to the range of the format that is being written - see getAsInt32().
+ */
+ inline void setAsInt32 (int32 newValue) noexcept
+ {
+ static_jassert (Constness::isConst == 0); // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead!
+ Endianness::setAsInt32 (data, newValue);
+ }
+
+ /** Moves the pointer along to the next sample. */
+ inline Pointer& operator++() noexcept { advance(); return *this; }
+
+ /** Moves the pointer back to the previous sample. */
+ inline Pointer& operator--() noexcept { this->advanceDataBy (data, -1); return *this; }
+
+ /** Adds a number of samples to the pointer's position. */
+ Pointer& operator+= (int samplesToJump) noexcept { this->advanceDataBy (data, samplesToJump); return *this; }
+
+ /** Writes a stream of samples into this pointer from another pointer.
+ This will copy the specified number of samples, converting between formats appropriately.
+ */
+ void convertSamples (Pointer source, int numSamples) const noexcept
+ {
+ static_jassert (Constness::isConst == 0); // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead!
+
+ for (Pointer dest (*this); --numSamples >= 0;)
+ {
+ dest.data.copyFromSameType (source.data);
+ dest.advance();
+ source.advance();
+ }
+ }
+
+ /** Writes a stream of samples into this pointer from another pointer.
+ This will copy the specified number of samples, converting between formats appropriately.
+ */
+ template
+ void convertSamples (OtherPointerType source, int numSamples) const noexcept
+ {
+ static_jassert (Constness::isConst == 0); // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead!
+
+ Pointer dest (*this);
+
+ if (source.getRawData() != getRawData() || source.getNumBytesBetweenSamples() >= getNumBytesBetweenSamples())
+ {
+ while (--numSamples >= 0)
+ {
+ Endianness::copyFrom (dest.data, source);
+ dest.advance();
+ ++source;
+ }
+ }
+ else // copy backwards if we're increasing the sample width..
+ {
+ dest += numSamples;
+ source += numSamples;
+
+ while (--numSamples >= 0)
+ Endianness::copyFrom ((--dest).data, --source);
+ }
+ }
+
+ /** Sets a number of samples to zero. */
+ void clearSamples (int numSamples) const noexcept
+ {
+ Pointer dest (*this);
+ dest.clear (dest.data, numSamples);
+ }
+
+ /** Scans a block of data, returning the lowest and highest levels as floats */
+ void findMinAndMax (size_t numSamples, float& minValue, float& maxValue) const noexcept
+ {
+ if (numSamples == 0)
+ {
+ minValue = maxValue = 0;
+ return;
+ }
+
+ Pointer dest (*this);
+
+ if (isFloatingPoint())
+ {
+ float mn = dest.getAsFloat();
+ dest.advance();
+ float mx = mn;
+
+ while (--numSamples > 0)
+ {
+ const float v = dest.getAsFloat();
+ dest.advance();
+
+ if (mx < v) mx = v;
+ if (v < mn) mn = v;
+ }
+
+ minValue = mn;
+ maxValue = mx;
+ }
+ else
+ {
+ int32 mn = dest.getAsInt32();
+ dest.advance();
+ int32 mx = mn;
+
+ while (--numSamples > 0)
+ {
+ const int v = dest.getAsInt32();
+ dest.advance();
+
+ if (mx < v) mx = v;
+ if (v < mn) mn = v;
+ }
+
+ minValue = mn * (float) (1.0 / (1.0 + Int32::maxValue));
+ maxValue = mx * (float) (1.0 / (1.0 + Int32::maxValue));
+ }
+ }
+
+ /** Returns true if the pointer is using a floating-point format. */
+ static bool isFloatingPoint() noexcept { return (bool) SampleFormat::isFloat; }
+
+ /** Returns true if the format is big-endian. */
+ static bool isBigEndian() noexcept { return (bool) Endianness::isBigEndian; }
+
+ /** Returns the number of bytes in each sample (ignoring the number of interleaved channels). */
+ static int getBytesPerSample() noexcept { return (int) SampleFormat::bytesPerSample; }
+
+ /** Returns the number of interleaved channels in the format. */
+ int getNumInterleavedChannels() const noexcept { return (int) this->numInterleavedChannels; }
+
+ /** Returns the number of bytes between the start address of each sample. */
+ int getNumBytesBetweenSamples() const noexcept { return InterleavingType::getNumBytesBetweenSamples (data); }
+
+ /** Returns the accuracy of this format when represented as a 32-bit integer.
+ This is the smallest number above 0 that can be represented in the sample format, converted to
+ a 32-bit range. E,g. if the format is 8-bit, its resolution is 0x01000000; if the format is 24-bit,
+ its resolution is 0x100.
+ */
+ static int get32BitResolution() noexcept { return (int) SampleFormat::resolution; }
+
+ /** Returns a pointer to the underlying data. */
+ const void* getRawData() const noexcept { return data.data; }
+
+ private:
+ //==============================================================================
+ SampleFormat data;
+
+ inline void advance() noexcept { this->advanceData (data); }
+
+ Pointer operator++ (int); // private to force you to use the more efficient pre-increment!
+ Pointer operator-- (int);
+ };
+
+ //==============================================================================
+ /** A base class for objects that are used to convert between two different sample formats.
+
+ The AudioData::ConverterInstance implements this base class and can be templated, so
+ you can create an instance that converts between two particular formats, and then
+ store this in the abstract base class.
+
+ @see AudioData::ConverterInstance
+ */
+ class Converter
+ {
+ public:
+ virtual ~Converter() {}
+
+ /** Converts a sequence of samples from the converter's source format into the dest format. */
+ virtual void convertSamples (void* destSamples, const void* sourceSamples, int numSamples) const = 0;
+
+ /** Converts a sequence of samples from the converter's source format into the dest format.
+ This method takes sub-channel indexes, which can be used with interleaved formats in order to choose a
+ particular sub-channel of the data to be used.
+ */
+ virtual void convertSamples (void* destSamples, int destSubChannel,
+ const void* sourceSamples, int sourceSubChannel, int numSamples) const = 0;
+ };
+
+ //==============================================================================
+ /**
+ A class that converts between two templated AudioData::Pointer types, and which
+ implements the AudioData::Converter interface.
+
+ This can be used as a concrete instance of the AudioData::Converter abstract class.
+
+ @see AudioData::Converter
+ */
+ template
+ class ConverterInstance : public Converter
+ {
+ public:
+ ConverterInstance (int numSourceChannels = 1, int numDestChannels = 1)
+ : sourceChannels (numSourceChannels), destChannels (numDestChannels)
+ {}
+
+ void convertSamples (void* dest, const void* source, int numSamples) const override
+ {
+ SourceSampleType s (source, sourceChannels);
+ DestSampleType d (dest, destChannels);
+ d.convertSamples (s, numSamples);
+ }
+
+ void convertSamples (void* dest, int destSubChannel,
+ const void* source, int sourceSubChannel, int numSamples) const override
+ {
+ jassert (destSubChannel < destChannels && sourceSubChannel < sourceChannels);
+
+ SourceSampleType s (addBytesToPointer (source, sourceSubChannel * SourceSampleType::getBytesPerSample()), sourceChannels);
+ DestSampleType d (addBytesToPointer (dest, destSubChannel * DestSampleType::getBytesPerSample()), destChannels);
+ d.convertSamples (s, numSamples);
+ }
+
+ private:
+ JUCE_DECLARE_NON_COPYABLE (ConverterInstance)
+
+ const int sourceChannels, destChannels;
+ };
+};
+
+
+
+//==============================================================================
+/**
+ A set of routines to convert buffers of 32-bit floating point data to and from
+ various integer formats.
+
+ Note that these functions are deprecated - the AudioData class provides a much more
+ flexible set of conversion classes now.
+*/
+class JUCE_API AudioDataConverters
+{
+public:
+ //==============================================================================
+ static void convertFloatToInt16LE (const float* source, void* dest, int numSamples, int destBytesPerSample = 2);
+ static void convertFloatToInt16BE (const float* source, void* dest, int numSamples, int destBytesPerSample = 2);
+
+ static void convertFloatToInt24LE (const float* source, void* dest, int numSamples, int destBytesPerSample = 3);
+ static void convertFloatToInt24BE (const float* source, void* dest, int numSamples, int destBytesPerSample = 3);
+
+ static void convertFloatToInt32LE (const float* source, void* dest, int numSamples, int destBytesPerSample = 4);
+ static void convertFloatToInt32BE (const float* source, void* dest, int numSamples, int destBytesPerSample = 4);
+
+ static void convertFloatToFloat32LE (const float* source, void* dest, int numSamples, int destBytesPerSample = 4);
+ static void convertFloatToFloat32BE (const float* source, void* dest, int numSamples, int destBytesPerSample = 4);
+
+ //==============================================================================
+ static void convertInt16LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 2);
+ static void convertInt16BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 2);
+
+ static void convertInt24LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 3);
+ static void convertInt24BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 3);
+
+ static void convertInt32LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 4);
+ static void convertInt32BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 4);
+
+ static void convertFloat32LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 4);
+ static void convertFloat32BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 4);
+
+ //==============================================================================
+ enum DataFormat
+ {
+ int16LE,
+ int16BE,
+ int24LE,
+ int24BE,
+ int32LE,
+ int32BE,
+ float32LE,
+ float32BE,
+ };
+
+ static void convertFloatToFormat (DataFormat destFormat,
+ const float* source, void* dest, int numSamples);
+
+ static void convertFormatToFloat (DataFormat sourceFormat,
+ const void* source, float* dest, int numSamples);
+
+ //==============================================================================
+ static void interleaveSamples (const float** source, float* dest,
+ int numSamples, int numChannels);
+
+ static void deinterleaveSamples (const float* source, float** dest,
+ int numSamples, int numChannels);
+
+private:
+ AudioDataConverters();
+ JUCE_DECLARE_NON_COPYABLE (AudioDataConverters)
+};
+
+
+#endif // JUCE_AUDIODATACONVERTERS_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp
new file mode 100644
index 0000000..15b59da
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp
@@ -0,0 +1,670 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+AudioSampleBuffer::AudioSampleBuffer() noexcept
+ : numChannels (0), size (0), allocatedBytes (0),
+ channels (static_cast (preallocatedChannelSpace)),
+ isClear (false)
+{
+}
+
+AudioSampleBuffer::AudioSampleBuffer (const int numChans,
+ const int numSamples) noexcept
+ : numChannels (numChans),
+ size (numSamples)
+{
+ jassert (numSamples >= 0);
+ jassert (numChans >= 0);
+
+ allocateData();
+}
+
+AudioSampleBuffer::AudioSampleBuffer (const AudioSampleBuffer& other) noexcept
+ : numChannels (other.numChannels),
+ size (other.size),
+ allocatedBytes (other.allocatedBytes)
+{
+ if (allocatedBytes == 0)
+ {
+ allocateChannels (other.channels, 0);
+ }
+ else
+ {
+ allocateData();
+
+ if (other.isClear)
+ {
+ clear();
+ }
+ else
+ {
+ for (int i = 0; i < numChannels; ++i)
+ FloatVectorOperations::copy (channels[i], other.channels[i], size);
+ }
+ }
+}
+
+void AudioSampleBuffer::allocateData()
+{
+ const size_t channelListSize = sizeof (float*) * (size_t) (numChannels + 1);
+ allocatedBytes = (size_t) numChannels * (size_t) size * sizeof (float) + channelListSize + 32;
+ allocatedData.malloc (allocatedBytes);
+ channels = reinterpret_cast (allocatedData.getData());
+
+ float* chan = (float*) (allocatedData + channelListSize);
+ for (int i = 0; i < numChannels; ++i)
+ {
+ channels[i] = chan;
+ chan += size;
+ }
+
+ channels [numChannels] = nullptr;
+ isClear = false;
+}
+
+AudioSampleBuffer::AudioSampleBuffer (float* const* dataToReferTo,
+ const int numChans,
+ const int numSamples) noexcept
+ : numChannels (numChans),
+ size (numSamples),
+ allocatedBytes (0)
+{
+ jassert (dataToReferTo != nullptr);
+ jassert (numChans >= 0 && numSamples >= 0);
+ allocateChannels (dataToReferTo, 0);
+}
+
+AudioSampleBuffer::AudioSampleBuffer (float* const* dataToReferTo,
+ const int numChans,
+ const int startSample,
+ const int numSamples) noexcept
+ : numChannels (numChans),
+ size (numSamples),
+ allocatedBytes (0),
+ isClear (false)
+{
+ jassert (dataToReferTo != nullptr);
+ jassert (numChans >= 0 && startSample >= 0 && numSamples >= 0);
+ allocateChannels (dataToReferTo, startSample);
+}
+
+void AudioSampleBuffer::setDataToReferTo (float** dataToReferTo,
+ const int newNumChannels,
+ const int newNumSamples) noexcept
+{
+ jassert (dataToReferTo != nullptr);
+ jassert (newNumChannels >= 0 && newNumSamples >= 0);
+
+ allocatedBytes = 0;
+ allocatedData.free();
+
+ numChannels = newNumChannels;
+ size = newNumSamples;
+
+ allocateChannels (dataToReferTo, 0);
+ jassert (! isClear);
+}
+
+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)
+ if (numChannels < (int) numElementsInArray (preallocatedChannelSpace))
+ {
+ channels = static_cast (preallocatedChannelSpace);
+ }
+ else
+ {
+ allocatedData.malloc ((size_t) numChannels + 1, sizeof (float*));
+ channels = reinterpret_cast (allocatedData.getData());
+ }
+
+ for (int i = 0; i < numChannels; ++i)
+ {
+ // you have to pass in the same number of valid pointers as numChannels
+ jassert (dataToReferTo[i] != nullptr);
+
+ channels[i] = dataToReferTo[i] + offset;
+ }
+
+ channels [numChannels] = nullptr;
+ isClear = false;
+}
+
+AudioSampleBuffer& AudioSampleBuffer::operator= (const AudioSampleBuffer& other) noexcept
+{
+ if (this != &other)
+ {
+ setSize (other.getNumChannels(), other.getNumSamples(), false, false, false);
+
+ if (other.isClear)
+ {
+ clear();
+ }
+ else
+ {
+ isClear = false;
+
+ for (int i = 0; i < numChannels; ++i)
+ FloatVectorOperations::copy (channels[i], other.channels[i], size);
+ }
+ }
+
+ return *this;
+}
+
+AudioSampleBuffer::~AudioSampleBuffer() noexcept
+{
+}
+
+void AudioSampleBuffer::setSize (const int newNumChannels,
+ const int newNumSamples,
+ const bool keepExistingContent,
+ const bool clearExtraSpace,
+ const bool avoidReallocating) noexcept
+{
+ jassert (newNumChannels >= 0);
+ jassert (newNumSamples >= 0);
+
+ if (newNumSamples != size || newNumChannels != numChannels)
+ {
+ const size_t allocatedSamplesPerChannel = ((size_t) newNumSamples + 3) & ~3u;
+ const size_t channelListSize = ((sizeof (float*) * (size_t) (newNumChannels + 1)) + 15) & ~15u;
+ const size_t newTotalBytes = ((size_t) newNumChannels * (size_t) allocatedSamplesPerChannel * sizeof (float))
+ + channelListSize + 32;
+
+ if (keepExistingContent)
+ {
+ HeapBlock newData;
+ newData.allocate (newTotalBytes, clearExtraSpace || isClear);
+
+ const size_t numSamplesToCopy = (size_t) jmin (newNumSamples, size);
+
+ float** const newChannels = reinterpret_cast (newData.getData());
+ float* newChan = reinterpret_cast (newData + channelListSize);
+
+ for (int j = 0; j < newNumChannels; ++j)
+ {
+ newChannels[j] = newChan;
+ newChan += allocatedSamplesPerChannel;
+ }
+
+ if (! isClear)
+ {
+ const int numChansToCopy = jmin (numChannels, newNumChannels);
+ for (int i = 0; i < numChansToCopy; ++i)
+ FloatVectorOperations::copy (newChannels[i], channels[i], (int) numSamplesToCopy);
+ }
+
+ allocatedData.swapWith (newData);
+ allocatedBytes = newTotalBytes;
+ channels = newChannels;
+ }
+ else
+ {
+ if (avoidReallocating && allocatedBytes >= newTotalBytes)
+ {
+ if (clearExtraSpace || isClear)
+ allocatedData.clear (newTotalBytes);
+ }
+ else
+ {
+ allocatedBytes = newTotalBytes;
+ allocatedData.allocate (newTotalBytes, clearExtraSpace || isClear);
+ channels = reinterpret_cast (allocatedData.getData());
+ }
+
+ float* chan = reinterpret_cast (allocatedData + channelListSize);
+ for (int i = 0; i < newNumChannels; ++i)
+ {
+ channels[i] = chan;
+ chan += allocatedSamplesPerChannel;
+ }
+ }
+
+ channels [newNumChannels] = 0;
+ size = newNumSamples;
+ numChannels = newNumChannels;
+ }
+}
+
+void AudioSampleBuffer::clear() noexcept
+{
+ if (! isClear)
+ {
+ for (int i = 0; i < numChannels; ++i)
+ FloatVectorOperations::clear (channels[i], size);
+
+ isClear = true;
+ }
+}
+
+void AudioSampleBuffer::clear (const int startSample,
+ const int numSamples) noexcept
+{
+ jassert (startSample >= 0 && startSample + numSamples <= size);
+
+ if (! isClear)
+ {
+ if (startSample == 0 && numSamples == size)
+ isClear = true;
+
+ for (int i = 0; i < numChannels; ++i)
+ FloatVectorOperations::clear (channels[i] + startSample, numSamples);
+ }
+}
+
+void AudioSampleBuffer::clear (const int channel,
+ const int startSample,
+ const int numSamples) noexcept
+{
+ jassert (isPositiveAndBelow (channel, numChannels));
+ jassert (startSample >= 0 && startSample + numSamples <= size);
+
+ if (! isClear)
+ FloatVectorOperations::clear (channels [channel] + startSample, numSamples);
+}
+
+float AudioSampleBuffer::getSample (int channel, int index) const noexcept
+{
+ jassert (isPositiveAndBelow (channel, numChannels));
+ jassert (isPositiveAndBelow (index, size));
+ return *(channels [channel] + index);
+}
+
+void AudioSampleBuffer::setSample (int channel, int index, float newValue) noexcept
+{
+ jassert (isPositiveAndBelow (channel, numChannels));
+ jassert (isPositiveAndBelow (index, size));
+ *(channels [channel] + index) = newValue;
+ isClear = false;
+}
+
+void AudioSampleBuffer::addSample (int channel, int index, float valueToAdd) noexcept
+{
+ jassert (isPositiveAndBelow (channel, numChannels));
+ jassert (isPositiveAndBelow (index, size));
+ *(channels [channel] + index) += valueToAdd;
+ isClear = false;
+}
+
+void AudioSampleBuffer::applyGain (const int channel,
+ const int startSample,
+ int numSamples,
+ const float gain) noexcept
+{
+ jassert (isPositiveAndBelow (channel, numChannels));
+ jassert (startSample >= 0 && startSample + numSamples <= size);
+
+ if (gain != 1.0f && ! isClear)
+ {
+ float* const d = channels [channel] + startSample;
+
+ if (gain == 0.0f)
+ FloatVectorOperations::clear (d, numSamples);
+ else
+ FloatVectorOperations::multiply (d, gain, numSamples);
+ }
+}
+
+void AudioSampleBuffer::applyGainRamp (const int channel,
+ const int startSample,
+ int numSamples,
+ float startGain,
+ float endGain) noexcept
+{
+ if (! isClear)
+ {
+ if (startGain == endGain)
+ {
+ applyGain (channel, startSample, numSamples, startGain);
+ }
+ else
+ {
+ jassert (isPositiveAndBelow (channel, numChannels));
+ jassert (startSample >= 0 && startSample + numSamples <= size);
+
+ const float increment = (endGain - startGain) / numSamples;
+ float* d = channels [channel] + startSample;
+
+ while (--numSamples >= 0)
+ {
+ *d++ *= startGain;
+ startGain += increment;
+ }
+ }
+ }
+}
+
+void AudioSampleBuffer::applyGain (int startSample, int numSamples, float gain) noexcept
+{
+ for (int i = 0; i < numChannels; ++i)
+ applyGain (i, startSample, numSamples, gain);
+}
+
+void AudioSampleBuffer::applyGain (const float gain) noexcept
+{
+ applyGain (0, size, gain);
+}
+
+void AudioSampleBuffer::applyGainRamp (int startSample, int numSamples,
+ float startGain, float endGain) noexcept
+{
+ for (int i = 0; i < numChannels; ++i)
+ applyGainRamp (i, startSample, numSamples, startGain, endGain);
+}
+
+void AudioSampleBuffer::addFrom (const int destChannel,
+ const int destStartSample,
+ const AudioSampleBuffer& source,
+ const int sourceChannel,
+ const int sourceStartSample,
+ int numSamples,
+ const float gain) noexcept
+{
+ jassert (&source != this || sourceChannel != destChannel);
+ jassert (isPositiveAndBelow (destChannel, numChannels));
+ jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
+ jassert (isPositiveAndBelow (sourceChannel, source.numChannels));
+ jassert (sourceStartSample >= 0 && sourceStartSample + numSamples <= source.size);
+
+ if (gain != 0.0f && numSamples > 0 && ! source.isClear)
+ {
+ float* const d = channels [destChannel] + destStartSample;
+ const float* const s = source.channels [sourceChannel] + sourceStartSample;
+
+ if (isClear)
+ {
+ isClear = false;
+
+ if (gain != 1.0f)
+ FloatVectorOperations::copyWithMultiply (d, s, gain, numSamples);
+ else
+ FloatVectorOperations::copy (d, s, numSamples);
+ }
+ else
+ {
+ if (gain != 1.0f)
+ FloatVectorOperations::addWithMultiply (d, s, gain, numSamples);
+ else
+ FloatVectorOperations::add (d, s, numSamples);
+ }
+ }
+}
+
+void AudioSampleBuffer::addFrom (const int destChannel,
+ const int destStartSample,
+ const float* source,
+ int numSamples,
+ const float gain) noexcept
+{
+ jassert (isPositiveAndBelow (destChannel, numChannels));
+ jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
+ jassert (source != nullptr);
+
+ if (gain != 0.0f && numSamples > 0)
+ {
+ float* const d = channels [destChannel] + destStartSample;
+
+ if (isClear)
+ {
+ isClear = false;
+
+ if (gain != 1.0f)
+ FloatVectorOperations::copyWithMultiply (d, source, gain, numSamples);
+ else
+ FloatVectorOperations::copy (d, source, numSamples);
+ }
+ else
+ {
+ if (gain != 1.0f)
+ FloatVectorOperations::addWithMultiply (d, source, gain, numSamples);
+ else
+ FloatVectorOperations::add (d, source, numSamples);
+ }
+ }
+}
+
+void AudioSampleBuffer::addFromWithRamp (const int destChannel,
+ const int destStartSample,
+ const float* source,
+ int numSamples,
+ float startGain,
+ const float endGain) noexcept
+{
+ jassert (isPositiveAndBelow (destChannel, numChannels));
+ jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
+ jassert (source != nullptr);
+
+ if (startGain == endGain)
+ {
+ addFrom (destChannel, destStartSample, source, numSamples, startGain);
+ }
+ else
+ {
+ if (numSamples > 0 && (startGain != 0.0f || endGain != 0.0f))
+ {
+ isClear = false;
+ const float increment = (endGain - startGain) / numSamples;
+ float* d = channels [destChannel] + destStartSample;
+
+ while (--numSamples >= 0)
+ {
+ *d++ += startGain * *source++;
+ startGain += increment;
+ }
+ }
+ }
+}
+
+void AudioSampleBuffer::copyFrom (const int destChannel,
+ const int destStartSample,
+ const AudioSampleBuffer& source,
+ const int sourceChannel,
+ const int sourceStartSample,
+ int numSamples) noexcept
+{
+ jassert (&source != this || sourceChannel != destChannel);
+ jassert (isPositiveAndBelow (destChannel, numChannels));
+ jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
+ jassert (isPositiveAndBelow (sourceChannel, source.numChannels));
+ jassert (sourceStartSample >= 0 && sourceStartSample + numSamples <= source.size);
+
+ if (numSamples > 0)
+ {
+ if (source.isClear)
+ {
+ if (! isClear)
+ FloatVectorOperations::clear (channels [destChannel] + destStartSample, numSamples);
+ }
+ else
+ {
+ isClear = false;
+ FloatVectorOperations::copy (channels [destChannel] + destStartSample,
+ source.channels [sourceChannel] + sourceStartSample,
+ numSamples);
+ }
+ }
+}
+
+void AudioSampleBuffer::copyFrom (const int destChannel,
+ const int destStartSample,
+ const float* source,
+ int numSamples) noexcept
+{
+ jassert (isPositiveAndBelow (destChannel, numChannels));
+ jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
+ jassert (source != nullptr);
+
+ if (numSamples > 0)
+ {
+ isClear = false;
+ FloatVectorOperations::copy (channels [destChannel] + destStartSample, source, numSamples);
+ }
+}
+
+void AudioSampleBuffer::copyFrom (const int destChannel,
+ const int destStartSample,
+ const float* source,
+ int numSamples,
+ const float gain) noexcept
+{
+ jassert (isPositiveAndBelow (destChannel, numChannels));
+ jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
+ jassert (source != nullptr);
+
+ if (numSamples > 0)
+ {
+ float* const d = channels [destChannel] + destStartSample;
+
+ if (gain != 1.0f)
+ {
+ if (gain == 0)
+ {
+ if (! isClear)
+ FloatVectorOperations::clear (d, numSamples);
+ }
+ else
+ {
+ isClear = false;
+ FloatVectorOperations::copyWithMultiply (d, source, gain, numSamples);
+ }
+ }
+ else
+ {
+ isClear = false;
+ FloatVectorOperations::copy (d, source, numSamples);
+ }
+ }
+}
+
+void AudioSampleBuffer::copyFromWithRamp (const int destChannel,
+ const int destStartSample,
+ const float* source,
+ int numSamples,
+ float startGain,
+ float endGain) noexcept
+{
+ jassert (isPositiveAndBelow (destChannel, numChannels));
+ jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
+ jassert (source != nullptr);
+
+ if (startGain == endGain)
+ {
+ copyFrom (destChannel, destStartSample, source, numSamples, startGain);
+ }
+ else
+ {
+ if (numSamples > 0 && (startGain != 0.0f || endGain != 0.0f))
+ {
+ isClear = false;
+ const float increment = (endGain - startGain) / numSamples;
+ float* d = channels [destChannel] + destStartSample;
+
+ while (--numSamples >= 0)
+ {
+ *d++ = startGain * *source++;
+ startGain += increment;
+ }
+ }
+ }
+}
+
+void AudioSampleBuffer::reverse (int channel, int startSample, int numSamples) const noexcept
+{
+ jassert (isPositiveAndBelow (channel, numChannels));
+ jassert (startSample >= 0 && startSample + numSamples <= size);
+
+ if (! isClear)
+ std::reverse (channels[channel] + startSample,
+ channels[channel] + startSample + numSamples);
+}
+
+void AudioSampleBuffer::reverse (int startSample, int numSamples) const noexcept
+{
+ for (int i = 0; i < numChannels; ++i)
+ reverse (i, startSample, numSamples);
+}
+
+Range AudioSampleBuffer::findMinMax (const int channel,
+ const int startSample,
+ int numSamples) const noexcept
+{
+ jassert (isPositiveAndBelow (channel, numChannels));
+ jassert (startSample >= 0 && startSample + numSamples <= size);
+
+ if (isClear)
+ return Range();
+
+ return FloatVectorOperations::findMinAndMax (channels [channel] + startSample, numSamples);
+}
+
+float AudioSampleBuffer::getMagnitude (const int channel,
+ const int startSample,
+ const int numSamples) const noexcept
+{
+ jassert (isPositiveAndBelow (channel, numChannels));
+ jassert (startSample >= 0 && startSample + numSamples <= size);
+
+ if (isClear)
+ return 0.0f;
+
+ const Range r (findMinMax (channel, startSample, numSamples));
+
+ return jmax (r.getStart(), -r.getStart(), r.getEnd(), -r.getEnd());
+}
+
+float AudioSampleBuffer::getMagnitude (int startSample, int numSamples) const noexcept
+{
+ float mag = 0.0f;
+
+ if (! isClear)
+ for (int i = 0; i < numChannels; ++i)
+ mag = jmax (mag, getMagnitude (i, startSample, numSamples));
+
+ return mag;
+}
+
+float AudioSampleBuffer::getRMSLevel (const int channel,
+ const int startSample,
+ const int numSamples) const noexcept
+{
+ jassert (isPositiveAndBelow (channel, numChannels));
+ jassert (startSample >= 0 && startSample + numSamples <= size);
+
+ if (numSamples <= 0 || channel < 0 || channel >= numChannels || isClear)
+ return 0.0f;
+
+ const float* const data = channels [channel] + startSample;
+ double sum = 0.0;
+
+ for (int i = 0; i < numSamples; ++i)
+ {
+ const float sample = data [i];
+ sum += sample * sample;
+ }
+
+ return (float) std::sqrt (sum / numSamples);
+}
diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h
new file mode 100644
index 0000000..8b9a72c
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h
@@ -0,0 +1,526 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_AUDIOSAMPLEBUFFER_H_INCLUDED
+#define JUCE_AUDIOSAMPLEBUFFER_H_INCLUDED
+
+
+//==============================================================================
+/**
+ A multi-channel buffer of 32-bit floating point audio samples.
+
+*/
+class JUCE_API AudioSampleBuffer
+{
+public:
+ //==============================================================================
+ /** Creates an empty buffer with 0 channels and 0 length. */
+ AudioSampleBuffer() noexcept;
+
+ //==============================================================================
+ /** Creates a buffer with a specified number of channels and samples.
+
+ The contents of the buffer will initially be undefined, so use clear() to
+ set all the samples to zero.
+
+ The buffer will allocate its memory internally, and this will be released
+ when the buffer is deleted. If the memory can't be allocated, this will
+ throw a std::bad_alloc exception.
+ */
+ AudioSampleBuffer (int numChannels,
+ int numSamples) noexcept;
+
+ /** Creates a buffer using a pre-allocated block of memory.
+
+ Note that if the buffer is resized or its number of channels is changed, it
+ will re-allocate memory internally and copy the existing data to this new area,
+ so it will then stop directly addressing this memory.
+
+ @param dataToReferTo a pre-allocated array containing pointers to the data
+ for each channel that should be used by this buffer. The
+ buffer will only refer to this memory, it won't try to delete
+ it when the buffer is deleted or resized.
+ @param numChannels the number of channels to use - this must correspond to the
+ number of elements in the array passed in
+ @param numSamples the number of samples to use - this must correspond to the
+ size of the arrays passed in
+ */
+ AudioSampleBuffer (float* const* dataToReferTo,
+ int numChannels,
+ int numSamples) noexcept;
+
+ /** Creates a buffer using a pre-allocated block of memory.
+
+ Note that if the buffer is resized or its number of channels is changed, it
+ will re-allocate memory internally and copy the existing data to this new area,
+ so it will then stop directly addressing this memory.
+
+ @param dataToReferTo a pre-allocated array containing pointers to the data
+ for each channel that should be used by this buffer. The
+ buffer will only refer to this memory, it won't try to delete
+ it when the buffer is deleted or resized.
+ @param numChannels the number of channels to use - this must correspond to the
+ number of elements in the array passed in
+ @param startSample the offset within the arrays at which the data begins
+ @param numSamples the number of samples to use - this must correspond to the
+ size of the arrays passed in
+ */
+ AudioSampleBuffer (float* const* dataToReferTo,
+ int numChannels,
+ int startSample,
+ int numSamples) noexcept;
+
+ /** Copies another buffer.
+
+ This buffer will make its own copy of the other's data, unless the buffer was created
+ using an external data buffer, in which case boths buffers will just point to the same
+ shared block of data.
+ */
+ AudioSampleBuffer (const AudioSampleBuffer&) noexcept;
+
+ /** Copies another buffer onto this one.
+ This buffer's size will be changed to that of the other buffer.
+ */
+ AudioSampleBuffer& operator= (const AudioSampleBuffer&) noexcept;
+
+ /** Destructor.
+ This will free any memory allocated by the buffer.
+ */
+ ~AudioSampleBuffer() noexcept;
+
+ //==============================================================================
+ /** Returns the number of channels of audio data that this buffer contains.
+ @see getSampleData
+ */
+ int getNumChannels() const noexcept { return numChannels; }
+
+ /** Returns the number of samples allocated in each of the buffer's channels.
+ @see getSampleData
+ */
+ int getNumSamples() const noexcept { return size; }
+
+ /** Returns a pointer to an array of read-only samples in one of the buffer's channels.
+ For speed, this doesn't check whether the channel number is out of range,
+ so be careful when using it!
+ If you need to write to the data, do NOT call this method and const_cast the
+ result! Instead, you must call getWritePointer so that the buffer knows you're
+ planning on modifying the data.
+ */
+ const float* getReadPointer (int channelNumber) const noexcept
+ {
+ jassert (isPositiveAndBelow (channelNumber, numChannels));
+ return channels [channelNumber];
+ }
+
+ /** Returns a pointer to an array of read-only samples in one of the buffer's channels.
+ For speed, this doesn't check whether the channel number or index are out of range,
+ so be careful when using it!
+ If you need to write to the data, do NOT call this method and const_cast the
+ result! Instead, you must call getWritePointer so that the buffer knows you're
+ planning on modifying the data.
+ */
+ const float* getReadPointer (int channelNumber, int sampleIndex) const noexcept
+ {
+ jassert (isPositiveAndBelow (channelNumber, numChannels));
+ jassert (isPositiveAndBelow (sampleIndex, size));
+ return channels [channelNumber] + sampleIndex;
+ }
+
+ /** Returns a writeable pointer to one of the buffer's channels.
+ For speed, this doesn't check whether the channel number is out of range,
+ so be careful when using it!
+ Note that if you're not planning on writing to the data, you should always
+ use getReadPointer instead.
+ */
+ float* getWritePointer (int channelNumber) noexcept
+ {
+ jassert (isPositiveAndBelow (channelNumber, numChannels));
+ isClear = false;
+ return channels [channelNumber];
+ }
+
+ /** Returns a writeable pointer to one of the buffer's channels.
+ For speed, this doesn't check whether the channel number or index are out of range,
+ so be careful when using it!
+ Note that if you're not planning on writing to the data, you should
+ use getReadPointer instead.
+ */
+ float* getWritePointer (int channelNumber, int sampleIndex) noexcept
+ {
+ jassert (isPositiveAndBelow (channelNumber, numChannels));
+ jassert (isPositiveAndBelow (sampleIndex, size));
+ isClear = false;
+ return channels [channelNumber] + sampleIndex;
+ }
+
+ /** Returns an array of pointers to the channels in the buffer.
+
+ Don't modify any of the pointers that are returned, and bear in mind that
+ these will become invalid if the buffer is resized.
+ */
+ const float** getArrayOfReadPointers() const noexcept { return const_cast (channels); }
+
+ /** Returns an array of pointers to the channels in the buffer.
+
+ Don't modify any of the pointers that are returned, and bear in mind that
+ these will become invalid if the buffer is resized.
+ */
+ float** getArrayOfWritePointers() noexcept { isClear = false; return channels; }
+
+ //==============================================================================
+ /** Changes the buffer's size or number of channels.
+
+ This can expand or contract the buffer's length, and add or remove channels.
+
+ If keepExistingContent is true, it will try to preserve as much of the
+ old data as it can in the new buffer.
+
+ If clearExtraSpace is true, then any extra channels or space that is
+ allocated will be also be cleared. If false, then this space is left
+ uninitialised.
+
+ If avoidReallocating is true, then changing the buffer's size won't reduce the
+ amount of memory that is currently allocated (but it will still increase it if
+ the new size is bigger than the amount it currently has). If this is false, then
+ a new allocation will be done so that the buffer uses takes up the minimum amount
+ of memory that it needs.
+
+ If the required memory can't be allocated, this will throw a std::bad_alloc exception.
+ */
+ void setSize (int newNumChannels,
+ int newNumSamples,
+ bool keepExistingContent = false,
+ bool clearExtraSpace = false,
+ bool avoidReallocating = false) noexcept;
+
+
+ /** Makes this buffer point to a pre-allocated set of channel data arrays.
+
+ There's also a constructor that lets you specify arrays like this, but this
+ lets you change the channels dynamically.
+
+ Note that if the buffer is resized or its number of channels is changed, it
+ will re-allocate memory internally and copy the existing data to this new area,
+ so it will then stop directly addressing this memory.
+
+ @param dataToReferTo a pre-allocated array containing pointers to the data
+ for each channel that should be used by this buffer. The
+ buffer will only refer to this memory, it won't try to delete
+ it when the buffer is deleted or resized.
+ @param numChannels the number of channels to use - this must correspond to the
+ number of elements in the array passed in
+ @param numSamples the number of samples to use - this must correspond to the
+ size of the arrays passed in
+ */
+ void setDataToReferTo (float** dataToReferTo,
+ int numChannels,
+ int numSamples) noexcept;
+
+ //==============================================================================
+ /** Clears all the samples in all channels. */
+ void clear() noexcept;
+
+ /** Clears a specified region of all the channels.
+
+ For speed, this doesn't check whether the channel and sample number
+ are in-range, so be careful!
+ */
+ void clear (int startSample,
+ int numSamples) noexcept;
+
+ /** Clears a specified region of just one channel.
+
+ For speed, this doesn't check whether the channel and sample number
+ are in-range, so be careful!
+ */
+ void clear (int channel,
+ int startSample,
+ int numSamples) noexcept;
+
+ /** Returns true if the buffer has been entirely cleared.
+ Note that this does not actually measure the contents of the buffer - it simply
+ returns a flag that is set when the buffer is cleared, and which is reset whenever
+ functions like getWritePointer() are invoked. That means the method does not take
+ any time, but it may return false negatives when in fact the buffer is still empty.
+ */
+ bool hasBeenCleared() const noexcept { return isClear; }
+
+ //==============================================================================
+ /** Returns a sample from the buffer.
+ The channel and index are not checked - they are expected to be in-range. If not,
+ an assertion will be thrown, but in a release build, you're into 'undefined behaviour'
+ territory.
+ */
+ float getSample (int channel, int sampleIndex) const noexcept;
+
+ /** Sets a sample in the buffer.
+ The channel and index are not checked - they are expected to be in-range. If not,
+ an assertion will be thrown, but in a release build, you're into 'undefined behaviour'
+ territory.
+ */
+ void setSample (int destChannel, int destSample, float newValue) noexcept;
+
+ /** Adds a value to a sample in the buffer.
+ The channel and index are not checked - they are expected to be in-range. If not,
+ an assertion will be thrown, but in a release build, you're into 'undefined behaviour'
+ territory.
+ */
+ void addSample (int destChannel, int destSample, float valueToAdd) noexcept;
+
+ /** Applies a gain multiple to a region of one channel.
+
+ For speed, this doesn't check whether the channel and sample number
+ are in-range, so be careful!
+ */
+ void applyGain (int channel,
+ int startSample,
+ int numSamples,
+ float gain) noexcept;
+
+ /** Applies a gain multiple to a region of all the channels.
+
+ For speed, this doesn't check whether the sample numbers
+ are in-range, so be careful!
+ */
+ void applyGain (int startSample,
+ int numSamples,
+ float gain) noexcept;
+
+ /** Applies a gain multiple to all the audio data. */
+ void applyGain (float gain) noexcept;
+
+ /** Applies a range of gains to a region of a channel.
+
+ The gain that is applied to each sample will vary from
+ startGain on the first sample to endGain on the last Sample,
+ so it can be used to do basic fades.
+
+ For speed, this doesn't check whether the sample numbers
+ are in-range, so be careful!
+ */
+ void applyGainRamp (int channel,
+ int startSample,
+ int numSamples,
+ float startGain,
+ float endGain) noexcept;
+
+ /** Applies a range of gains to a region of all channels.
+
+ The gain that is applied to each sample will vary from
+ startGain on the first sample to endGain on the last Sample,
+ so it can be used to do basic fades.
+
+ For speed, this doesn't check whether the sample numbers
+ are in-range, so be careful!
+ */
+ void applyGainRamp (int startSample,
+ int numSamples,
+ float startGain,
+ float endGain) noexcept;
+
+ /** Adds samples from another buffer to this one.
+
+ @param destChannel the channel within this buffer to add the samples to
+ @param destStartSample the start sample within this buffer's channel
+ @param source the source buffer to add from
+ @param sourceChannel the channel within the source buffer to read from
+ @param sourceStartSample the offset within the source buffer's channel to start reading samples from
+ @param numSamples the number of samples to process
+ @param gainToApplyToSource an optional gain to apply to the source samples before they are
+ added to this buffer's samples
+
+ @see copyFrom
+ */
+ void addFrom (int destChannel,
+ int destStartSample,
+ const AudioSampleBuffer& source,
+ int sourceChannel,
+ int sourceStartSample,
+ int numSamples,
+ float gainToApplyToSource = 1.0f) noexcept;
+
+ /** Adds samples from an array of floats to one of the channels.
+
+ @param destChannel the channel within this buffer to add the samples to
+ @param destStartSample the start sample within this buffer's channel
+ @param source the source data to use
+ @param numSamples the number of samples to process
+ @param gainToApplyToSource an optional gain to apply to the source samples before they are
+ added to this buffer's samples
+
+ @see copyFrom
+ */
+ void addFrom (int destChannel,
+ int destStartSample,
+ const float* source,
+ int numSamples,
+ float gainToApplyToSource = 1.0f) noexcept;
+
+ /** Adds samples from an array of floats, applying a gain ramp to them.
+
+ @param destChannel the channel within this buffer to add the samples to
+ @param destStartSample the start sample within this buffer's channel
+ @param source the source data to use
+ @param numSamples the number of samples to process
+ @param startGain the gain to apply to the first sample (this is multiplied with
+ the source samples before they are added to this buffer)
+ @param endGain the gain to apply to the final sample. The gain is linearly
+ interpolated between the first and last samples.
+ */
+ void addFromWithRamp (int destChannel,
+ int destStartSample,
+ const float* source,
+ int numSamples,
+ float startGain,
+ float endGain) noexcept;
+
+ /** Copies samples from another buffer to this one.
+
+ @param destChannel the channel within this buffer to copy the samples to
+ @param destStartSample the start sample within this buffer's channel
+ @param source the source buffer to read from
+ @param sourceChannel the channel within the source buffer to read from
+ @param sourceStartSample the offset within the source buffer's channel to start reading samples from
+ @param numSamples the number of samples to process
+
+ @see addFrom
+ */
+ void copyFrom (int destChannel,
+ int destStartSample,
+ const AudioSampleBuffer& source,
+ int sourceChannel,
+ int sourceStartSample,
+ int numSamples) noexcept;
+
+ /** Copies samples from an array of floats into one of the channels.
+
+ @param destChannel the channel within this buffer to copy the samples to
+ @param destStartSample the start sample within this buffer's channel
+ @param source the source buffer to read from
+ @param numSamples the number of samples to process
+
+ @see addFrom
+ */
+ void copyFrom (int destChannel,
+ int destStartSample,
+ const float* source,
+ int numSamples) noexcept;
+
+ /** Copies samples from an array of floats into one of the channels, applying a gain to it.
+
+ @param destChannel the channel within this buffer to copy the samples to
+ @param destStartSample the start sample within this buffer's channel
+ @param source the source buffer to read from
+ @param numSamples the number of samples to process
+ @param gain the gain to apply
+
+ @see addFrom
+ */
+ void copyFrom (int destChannel,
+ int destStartSample,
+ const float* source,
+ int numSamples,
+ float gain) noexcept;
+
+ /** Copies samples from an array of floats into one of the channels, applying a gain ramp.
+
+ @param destChannel the channel within this buffer to copy the samples to
+ @param destStartSample the start sample within this buffer's channel
+ @param source the source buffer to read from
+ @param numSamples the number of samples to process
+ @param startGain the gain to apply to the first sample (this is multiplied with
+ the source samples before they are copied to this buffer)
+ @param endGain the gain to apply to the final sample. The gain is linearly
+ interpolated between the first and last samples.
+
+ @see addFrom
+ */
+ void copyFromWithRamp (int destChannel,
+ int destStartSample,
+ const float* source,
+ int numSamples,
+ float startGain,
+ float endGain) noexcept;
+
+
+ /** Returns a Range indicating the lowest and highest sample values in a given section.
+
+ @param channel the channel to read from
+ @param startSample the start sample within the channel
+ @param numSamples the number of samples to check
+ */
+ Range findMinMax (int channel,
+ int startSample,
+ int numSamples) const noexcept;
+
+ /** Finds the highest absolute sample value within a region of a channel. */
+ float getMagnitude (int channel,
+ int startSample,
+ int numSamples) const noexcept;
+
+ /** Finds the highest absolute sample value within a region on all channels. */
+ float getMagnitude (int startSample,
+ int numSamples) const noexcept;
+
+ /** Returns the root mean squared level for a region of a channel. */
+ float getRMSLevel (int channel,
+ int startSample,
+ int numSamples) const noexcept;
+
+ /** Reverses a part of a channel. */
+ void reverse (int channel, int startSample, int numSamples) const noexcept;
+
+ /** Reverses a part of the buffer. */
+ void reverse (int startSample, int numSamples) const noexcept;
+
+ //==============================================================================
+ #ifndef DOXYGEN
+ // Note that these methods have now been replaced by getReadPointer() and getWritePointer()
+ JUCE_DEPRECATED_WITH_BODY (const float* getSampleData (int channel) const, { return getReadPointer (channel); })
+ JUCE_DEPRECATED_WITH_BODY (const float* getSampleData (int channel, int index) const, { return getReadPointer (channel, index); })
+ JUCE_DEPRECATED_WITH_BODY (float* getSampleData (int channel), { return getWritePointer (channel); })
+ JUCE_DEPRECATED_WITH_BODY (float* getSampleData (int channel, int index), { return getWritePointer (channel, index); })
+
+ // These have been replaced by getArrayOfReadPointers() and getArrayOfWritePointers()
+ JUCE_DEPRECATED_WITH_BODY (const float** getArrayOfChannels() const, { return getArrayOfReadPointers(); })
+ JUCE_DEPRECATED_WITH_BODY (float** getArrayOfChannels(), { return getArrayOfWritePointers(); })
+ #endif
+
+private:
+ //==============================================================================
+ int numChannels, size;
+ size_t allocatedBytes;
+ float** channels;
+ HeapBlock allocatedData;
+ float* preallocatedChannelSpace [32];
+ bool isClear;
+
+ void allocateData();
+ void allocateChannels (float* const*, int offset);
+
+ JUCE_LEAK_DETECTOR (AudioSampleBuffer)
+};
+
+
+#endif // JUCE_AUDIOSAMPLEBUFFER_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp
new file mode 100644
index 0000000..2fc6898
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp
@@ -0,0 +1,893 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+namespace FloatVectorHelpers
+{
+ #define JUCE_INCREMENT_SRC_DEST dest += (16 / sizeof (*dest)); src += (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
+ static bool sse2Present = false;
+
+ static bool isSSE2Available() noexcept
+ {
+ if (sse2Present)
+ return true;
+
+ sse2Present = SystemStats::hasSSE2();
+ return sse2Present;
+ }
+
+ inline static bool isAligned (const void* p) noexcept
+ {
+ return (((pointer_sized_int) p) & 15) == 0;
+ }
+
+ struct BasicOps32
+ {
+ typedef float Type;
+ typedef __m128 ParallelType;
+ enum { numParallel = 4 };
+
+ static forcedinline ParallelType load1 (Type v) noexcept { return _mm_load1_ps (&v); }
+ static forcedinline ParallelType loadA (const Type* v) noexcept { return _mm_load_ps (v); }
+ static forcedinline ParallelType loadU (const Type* v) noexcept { return _mm_loadu_ps (v); }
+ static forcedinline void storeA (Type* dest, ParallelType a) noexcept { _mm_store_ps (dest, a); }
+ static forcedinline void storeU (Type* dest, ParallelType a) noexcept { _mm_storeu_ps (dest, a); }
+
+ static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return _mm_add_ps (a, b); }
+ static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return _mm_sub_ps (a, b); }
+ static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return _mm_mul_ps (a, b); }
+ static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return _mm_max_ps (a, b); }
+ static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return _mm_min_ps (a, b); }
+
+ static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1], v[2], v[3]); }
+ static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1], v[2], v[3]); }
+ };
+
+ struct BasicOps64
+ {
+ typedef double Type;
+ typedef __m128d ParallelType;
+ enum { numParallel = 2 };
+
+ static forcedinline ParallelType load1 (Type v) noexcept { return _mm_load1_pd (&v); }
+ static forcedinline ParallelType loadA (const Type* v) noexcept { return _mm_load_pd (v); }
+ static forcedinline ParallelType loadU (const Type* v) noexcept { return _mm_loadu_pd (v); }
+ static forcedinline void storeA (Type* dest, ParallelType a) noexcept { _mm_store_pd (dest, a); }
+ static forcedinline void storeU (Type* dest, ParallelType a) noexcept { _mm_storeu_pd (dest, a); }
+
+ static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return _mm_add_pd (a, b); }
+ static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return _mm_sub_pd (a, b); }
+ static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return _mm_mul_pd (a, b); }
+ static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return _mm_max_pd (a, b); }
+ static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return _mm_min_pd (a, b); }
+
+ static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1]); }
+ static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1]); }
+ };
+
+ #define JUCE_BEGIN_VEC_OP \
+ typedef FloatVectorHelpers::ModeType::Mode Mode; \
+ if (FloatVectorHelpers::isSSE2Available()) \
+ { \
+ const int numLongOps = num / Mode::numParallel;
+
+ #define JUCE_FINISH_VEC_OP(normalOp) \
+ num &= (Mode::numParallel - 1); \
+ if (num == 0) return; \
+ } \
+ for (int i = 0; i < num; ++i) normalOp;
+
+ #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \
+ JUCE_BEGIN_VEC_OP \
+ setupOp \
+ if (FloatVectorHelpers::isAligned (dest)) JUCE_VEC_LOOP (vecOp, dummy, Mode::loadA, Mode::storeA, locals, JUCE_INCREMENT_DEST) \
+ else JUCE_VEC_LOOP (vecOp, dummy, Mode::loadU, Mode::storeU, locals, JUCE_INCREMENT_DEST) \
+ JUCE_FINISH_VEC_OP (normalOp)
+
+ #define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \
+ JUCE_BEGIN_VEC_OP \
+ setupOp \
+ if (FloatVectorHelpers::isAligned (dest)) \
+ { \
+ if (FloatVectorHelpers::isAligned (src)) JUCE_VEC_LOOP (vecOp, Mode::loadA, Mode::loadA, Mode::storeA, locals, increment) \
+ else JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadA, Mode::storeA, locals, increment) \
+ }\
+ else \
+ { \
+ if (FloatVectorHelpers::isAligned (src)) JUCE_VEC_LOOP (vecOp, Mode::loadA, Mode::loadU, Mode::storeU, locals, increment) \
+ else JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \
+ } \
+ 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
+
+ struct BasicOps32
+ {
+ typedef float Type;
+ typedef float32x4_t ParallelType;
+ enum { numParallel = 4 };
+
+ static forcedinline ParallelType load1 (Type v) noexcept { return vld1q_dup_f32 (&v); }
+ static forcedinline ParallelType loadA (const Type* v) noexcept { return vld1q_f32 (v); }
+ static forcedinline ParallelType loadU (const Type* v) noexcept { return vld1q_f32 (v); }
+ static forcedinline void storeA (Type* dest, ParallelType a) noexcept { vst1q_f32 (dest, a); }
+ static forcedinline void storeU (Type* dest, ParallelType a) noexcept { vst1q_f32 (dest, a); }
+
+ static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return vaddq_f32 (a, b); }
+ static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return vsubq_f32 (a, b); }
+ static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return vmulq_f32 (a, b); }
+ static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return vmaxq_f32 (a, b); }
+ static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return vminq_f32 (a, b); }
+
+ static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1], v[2], v[3]); }
+ static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1], v[2], v[3]); }
+ };
+
+ struct BasicOps64
+ {
+ typedef double Type;
+ typedef double ParallelType;
+ enum { numParallel = 1 };
+
+ static forcedinline ParallelType load1 (Type v) noexcept { return v; }
+ static forcedinline ParallelType loadA (const Type* v) noexcept { return *v; }
+ static forcedinline ParallelType loadU (const Type* v) noexcept { return *v; }
+ static forcedinline void storeA (Type* dest, ParallelType a) noexcept { *dest = a; }
+ static forcedinline void storeU (Type* dest, ParallelType a) noexcept { *dest = a; }
+
+ static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return a + b; }
+ static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return a - b; }
+ static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return a * b; }
+ static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return jmax (a, b); }
+ static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return jmin (a, b); }
+
+ static forcedinline Type max (ParallelType a) noexcept { return a; }
+ static forcedinline Type min (ParallelType a) noexcept { return a; }
+ };
+
+ #define JUCE_BEGIN_VEC_OP \
+ typedef FloatVectorHelpers::ModeType::Mode Mode; \
+ if (Mode::numParallel > 1) \
+ { \
+ const int numLongOps = num / Mode::numParallel;
+
+ #define JUCE_FINISH_VEC_OP(normalOp) \
+ num &= (Mode::numParallel - 1); \
+ if (num == 0) return; \
+ } \
+ for (int i = 0; i < num; ++i) normalOp;
+
+ #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \
+ JUCE_BEGIN_VEC_OP \
+ setupOp \
+ JUCE_VEC_LOOP (vecOp, dummy, Mode::loadU, Mode::storeU, locals, JUCE_INCREMENT_DEST) \
+ JUCE_FINISH_VEC_OP (normalOp)
+
+ #define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \
+ JUCE_BEGIN_VEC_OP \
+ setupOp \
+ JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \
+ 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
+ #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \
+ for (int i = 0; i < num; ++i) normalOp;
+
+ #define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \
+ 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
+
+ //==============================================================================
+ #define JUCE_VEC_LOOP(vecOp, srcLoad, dstLoad, dstStore, locals, increment) \
+ for (int i = 0; i < numLongOps; ++i) \
+ { \
+ locals (srcLoad, dstLoad); \
+ dstStore (dest, vecOp); \
+ 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_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest);
+ #define JUCE_LOAD_SRC(srcLoad, dstLoad) const Mode::ParallelType s = srcLoad (src);
+ #define JUCE_LOAD_SRC1_SRC2(src1Load, src2Load) const Mode::ParallelType s1 = src1Load (src1), s2 = src2Load (src2);
+ #define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest), s = srcLoad (src);
+
+ #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON
+ template struct ModeType { typedef BasicOps32 Mode; };
+ template<> struct ModeType<8> { typedef BasicOps64 Mode; };
+
+ template
+ struct MinMax
+ {
+ typedef typename Mode::Type Type;
+ typedef typename Mode::ParallelType ParallelType;
+
+ static Type findMinOrMax (const Type* src, int num, const bool isMinimum) noexcept
+ {
+ int numLongOps = num / Mode::numParallel;
+
+ #if JUCE_USE_SSE_INTRINSICS
+ if (numLongOps > 1 && isSSE2Available())
+ #else
+ if (numLongOps > 1)
+ #endif
+ {
+ ParallelType val;
+
+ #if ! JUCE_USE_ARM_NEON
+ if (isAligned (src))
+ {
+ val = Mode::loadA (src);
+
+ if (isMinimum)
+ {
+ while (--numLongOps > 0)
+ {
+ src += Mode::numParallel;
+ val = Mode::min (val, Mode::loadA (src));
+ }
+ }
+ else
+ {
+ while (--numLongOps > 0)
+ {
+ src += Mode::numParallel;
+ val = Mode::max (val, Mode::loadA (src));
+ }
+ }
+ }
+ else
+ #endif
+ {
+ val = Mode::loadU (src);
+
+ if (isMinimum)
+ {
+ while (--numLongOps > 0)
+ {
+ src += Mode::numParallel;
+ val = Mode::min (val, Mode::loadU (src));
+ }
+ }
+ else
+ {
+ while (--numLongOps > 0)
+ {
+ src += Mode::numParallel;
+ val = Mode::max (val, Mode::loadU (src));
+ }
+ }
+ }
+
+ Type result = isMinimum ? Mode::min (val)
+ : Mode::max (val);
+
+ num &= (Mode::numParallel - 1);
+ src += Mode::numParallel;
+
+ for (int i = 0; i < num; ++i)
+ result = isMinimum ? jmin (result, src[i])
+ : jmax (result, src[i]);
+
+ return result;
+ }
+
+ return isMinimum ? juce::findMinimum (src, num)
+ : juce::findMaximum (src, num);
+ }
+
+ static Range findMinAndMax (const Type* src, int num) noexcept
+ {
+ int numLongOps = num / Mode::numParallel;
+
+ #if JUCE_USE_SSE_INTRINSICS
+ if (numLongOps > 1 && isSSE2Available())
+ #else
+ if (numLongOps > 1)
+ #endif
+ {
+ ParallelType mn, mx;
+
+ #if ! JUCE_USE_ARM_NEON
+ if (isAligned (src))
+ {
+ mn = Mode::loadA (src);
+ mx = mn;
+
+ while (--numLongOps > 0)
+ {
+ src += Mode::numParallel;
+ const ParallelType v = Mode::loadA (src);
+ mn = Mode::min (mn, v);
+ mx = Mode::max (mx, v);
+ }
+ }
+ else
+ #endif
+ {
+ mn = Mode::loadU (src);
+ mx = mn;
+
+ while (--numLongOps > 0)
+ {
+ src += Mode::numParallel;
+ const ParallelType v = Mode::loadU (src);
+ mn = Mode::min (mn, v);
+ mx = Mode::max (mx, v);
+ }
+ }
+
+ Range result (Mode::min (mn),
+ Mode::max (mx));
+
+ num &= (Mode::numParallel - 1);
+ src += Mode::numParallel;
+
+ for (int i = 0; i < num; ++i)
+ result = result.getUnionWith (src[i]);
+
+ return result;
+ }
+
+ return Range::findMinAndMax (src, num);
+ }
+ };
+ #endif
+}
+
+//==============================================================================
+void JUCE_CALLTYPE FloatVectorOperations::clear (float* dest, int num) noexcept
+{
+ #if JUCE_USE_VDSP_FRAMEWORK
+ vDSP_vclr (dest, 1, (size_t) num);
+ #else
+ zeromem (dest, num * sizeof (float));
+ #endif
+}
+
+void JUCE_CALLTYPE FloatVectorOperations::clear (double* dest, int num) noexcept
+{
+ #if JUCE_USE_VDSP_FRAMEWORK
+ vDSP_vclrD (dest, 1, (size_t) num);
+ #else
+ zeromem (dest, num * sizeof (double));
+ #endif
+}
+
+void JUCE_CALLTYPE FloatVectorOperations::fill (float* dest, float valueToFill, int num) noexcept
+{
+ #if JUCE_USE_VDSP_FRAMEWORK
+ vDSP_vfill (&valueToFill, dest, 1, (size_t) num);
+ #else
+ JUCE_PERFORM_VEC_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE,
+ const Mode::ParallelType val = Mode::load1 (valueToFill);)
+ #endif
+}
+
+void JUCE_CALLTYPE FloatVectorOperations::fill (double* dest, double valueToFill, int num) noexcept
+{
+ #if JUCE_USE_VDSP_FRAMEWORK
+ vDSP_vfillD (&valueToFill, dest, 1, (size_t) num);
+ #else
+ JUCE_PERFORM_VEC_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE,
+ const Mode::ParallelType val = Mode::load1 (valueToFill);)
+ #endif
+}
+
+void JUCE_CALLTYPE FloatVectorOperations::copy (float* dest, const float* src, int num) noexcept
+{
+ memcpy (dest, src, (size_t) num * sizeof (float));
+}
+
+void JUCE_CALLTYPE FloatVectorOperations::copy (double* dest, const double* src, int num) noexcept
+{
+ memcpy (dest, src, (size_t) num * sizeof (double));
+}
+
+void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept
+{
+ #if JUCE_USE_VDSP_FRAMEWORK
+ vDSP_vsmul (src, 1, &multiplier, dest, 1, (vDSP_Length) num);
+ #else
+ 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);)
+ #endif
+}
+
+void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept
+{
+ #if JUCE_USE_VDSP_FRAMEWORK
+ vDSP_vsmulD (src, 1, &multiplier, dest, 1, (vDSP_Length) num);
+ #else
+ 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);)
+ #endif
+}
+
+void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float amount, int num) noexcept
+{
+ JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, Mode::add (d, amountToAdd), JUCE_LOAD_DEST,
+ const Mode::ParallelType amountToAdd = Mode::load1 (amount);)
+}
+
+void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, double amount, int num) noexcept
+{
+ JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, Mode::add (d, amountToAdd), JUCE_LOAD_DEST,
+ 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
+{
+ #if JUCE_USE_VDSP_FRAMEWORK
+ vDSP_vadd (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
+ #else
+ JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
+ #endif
+}
+
+void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src, int num) noexcept
+{
+ #if JUCE_USE_VDSP_FRAMEWORK
+ vDSP_vaddD (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
+ #else
+ JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
+ #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
+{
+ #if JUCE_USE_VDSP_FRAMEWORK
+ vDSP_vsub (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
+ #else
+ JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
+ #endif
+}
+
+void JUCE_CALLTYPE FloatVectorOperations::subtract (double* dest, const double* src, int num) noexcept
+{
+ #if JUCE_USE_VDSP_FRAMEWORK
+ vDSP_vsubD (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
+ #else
+ JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
+ #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
+{
+ JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)),
+ JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST,
+ const Mode::ParallelType mult = Mode::load1 (multiplier);)
+}
+
+void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept
+{
+ JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)),
+ JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST,
+ const Mode::ParallelType mult = Mode::load1 (multiplier);)
+}
+
+void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, int num) noexcept
+{
+ #if JUCE_USE_VDSP_FRAMEWORK
+ vDSP_vmul (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
+ #else
+ JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
+ #endif
+}
+
+void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src, int num) noexcept
+{
+ #if JUCE_USE_VDSP_FRAMEWORK
+ vDSP_vmulD (src, 1, dest, 1, dest, 1, (vDSP_Length) num);
+ #else
+ JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, )
+ #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
+{
+ #if JUCE_USE_VDSP_FRAMEWORK
+ vDSP_vsmul (dest, 1, &multiplier, dest, 1, (vDSP_Length) num);
+ #else
+ JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST,
+ const Mode::ParallelType mult = Mode::load1 (multiplier);)
+ #endif
+}
+
+void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, double multiplier, int num) noexcept
+{
+ #if JUCE_USE_VDSP_FRAMEWORK
+ vDSP_vsmulD (dest, 1, &multiplier, dest, 1, (vDSP_Length) num);
+ #else
+ JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST,
+ const Mode::ParallelType mult = Mode::load1 (multiplier);)
+ #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
+{
+ #if JUCE_USE_VDSP_FRAMEWORK
+ vDSP_vneg ((float*) src, 1, dest, 1, (vDSP_Length) num);
+ #else
+ copyWithMultiply (dest, src, -1.0f, num);
+ #endif
+}
+
+void FloatVectorOperations::negate (double* dest, const double* src, int num) noexcept
+{
+ #if JUCE_USE_VDSP_FRAMEWORK
+ vDSP_vnegD ((double*) src, 1, dest, 1, (vDSP_Length) num);
+ #else
+ copyWithMultiply (dest, src, -1.0f, num);
+ #endif
+}
+
+void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, const int* src, float multiplier, int num) noexcept
+{
+ #if JUCE_USE_ARM_NEON
+ JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier,
+ vmulq_n_f32 (vcvtq_f32_s32 (vld1q_s32 (src)), multiplier),
+ JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST, )
+ #else
+ JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier,
+ Mode::mul (mult, _mm_cvtepi32_ps (_mm_loadu_si128 ((const __m128i*) src))),
+ JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST,
+ const Mode::ParallelType mult = Mode::load1 (multiplier);)
+ #endif
+}
+
+Range JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const float* src, int num) noexcept
+{
+ #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON
+ return FloatVectorHelpers::MinMax::findMinAndMax (src, num);
+ #else
+ return Range::findMinAndMax (src, num);
+ #endif
+}
+
+Range JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const double* src, int num) noexcept
+{
+ #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON
+ return FloatVectorHelpers::MinMax::findMinAndMax (src, num);
+ #else
+ return Range::findMinAndMax (src, num);
+ #endif
+}
+
+float JUCE_CALLTYPE FloatVectorOperations::findMinimum (const float* src, int num) noexcept
+{
+ #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON
+ return FloatVectorHelpers::MinMax::findMinOrMax (src, num, true);
+ #else
+ return juce::findMinimum (src, num);
+ #endif
+}
+
+double JUCE_CALLTYPE FloatVectorOperations::findMinimum (const double* src, int num) noexcept
+{
+ #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON
+ return FloatVectorHelpers::MinMax::findMinOrMax (src, num, true);
+ #else
+ return juce::findMinimum (src, num);
+ #endif
+}
+
+float JUCE_CALLTYPE FloatVectorOperations::findMaximum (const float* src, int num) noexcept
+{
+ #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON
+ return FloatVectorHelpers::MinMax::findMinOrMax (src, num, false);
+ #else
+ return juce::findMaximum (src, num);
+ #endif
+}
+
+double JUCE_CALLTYPE FloatVectorOperations::findMaximum (const double* src, int num) noexcept
+{
+ #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON
+ return FloatVectorHelpers::MinMax::findMinOrMax (src, num, false);
+ #else
+ return juce::findMaximum (src, num);
+ #endif
+}
+
+void JUCE_CALLTYPE FloatVectorOperations::enableFlushToZeroMode (bool shouldEnable) noexcept
+{
+ #if JUCE_USE_SSE_INTRINSICS
+ if (FloatVectorHelpers::isSSE2Available())
+ _MM_SET_FLUSH_ZERO_MODE (shouldEnable ? _MM_FLUSH_ZERO_ON : _MM_FLUSH_ZERO_OFF);
+ #endif
+ (void) shouldEnable;
+}
+
+//==============================================================================
+//==============================================================================
+#if JUCE_UNIT_TESTS
+
+class FloatVectorOperationsTests : public UnitTest
+{
+public:
+ FloatVectorOperationsTests() : UnitTest ("FloatVectorOperations") {}
+
+ template
+ struct TestRunner
+ {
+ static void runTest (UnitTest& u, Random random)
+ {
+ const int range = random.nextBool() ? 500 : 10;
+ const int num = random.nextInt (range) + 1;
+
+ HeapBlock buffer1 ((size_t) num + 16), buffer2 ((size_t) num + 16);
+ HeapBlock buffer3 ((size_t) num + 16);
+
+ #if JUCE_ARM
+ ValueType* const data1 = buffer1;
+ ValueType* const data2 = buffer2;
+ int* const int1 = buffer3;
+ #else
+ ValueType* const data1 = addBytesToPointer (buffer1.getData(), random.nextInt (16));
+ ValueType* const data2 = addBytesToPointer (buffer2.getData(), random.nextInt (16));
+ int* const int1 = addBytesToPointer (buffer3.getData(), random.nextInt (16));
+ #endif
+
+ fillRandomly (random, data1, num);
+ fillRandomly (random, data2, num);
+
+ Range minMax1 (FloatVectorOperations::findMinAndMax (data1, num));
+ Range minMax2 (Range::findMinAndMax (data1, num));
+ u.expect (minMax1 == minMax2);
+
+ u.expect (valuesMatch (FloatVectorOperations::findMinimum (data1, num), juce::findMinimum (data1, num)));
+ u.expect (valuesMatch (FloatVectorOperations::findMaximum (data1, num), juce::findMaximum (data1, num)));
+
+ u.expect (valuesMatch (FloatVectorOperations::findMinimum (data2, num), juce::findMinimum (data2, num)));
+ u.expect (valuesMatch (FloatVectorOperations::findMaximum (data2, num), juce::findMaximum (data2, num)));
+
+ FloatVectorOperations::clear (data1, num);
+ u.expect (areAllValuesEqual (data1, num, 0));
+
+ FloatVectorOperations::fill (data1, (ValueType) 2, num);
+ u.expect (areAllValuesEqual (data1, num, (ValueType) 2));
+
+ FloatVectorOperations::add (data1, (ValueType) 2, num);
+ u.expect (areAllValuesEqual (data1, num, (ValueType) 4));
+
+ FloatVectorOperations::copy (data2, data1, num);
+ u.expect (areAllValuesEqual (data2, num, (ValueType) 4));
+
+ FloatVectorOperations::add (data2, data1, num);
+ u.expect (areAllValuesEqual (data2, num, (ValueType) 8));
+
+ FloatVectorOperations::copyWithMultiply (data2, data1, (ValueType) 4, num);
+ u.expect (areAllValuesEqual (data2, num, (ValueType) 16));
+
+ FloatVectorOperations::addWithMultiply (data2, data1, (ValueType) 4, num);
+ u.expect (areAllValuesEqual (data2, num, (ValueType) 32));
+
+ FloatVectorOperations::multiply (data1, (ValueType) 2, num);
+ u.expect (areAllValuesEqual (data1, num, (ValueType) 8));
+
+ FloatVectorOperations::multiply (data1, data2, num);
+ u.expect (areAllValuesEqual (data1, num, (ValueType) 256));
+
+ FloatVectorOperations::negate (data2, data1, num);
+ u.expect (areAllValuesEqual (data2, num, (ValueType) -256));
+
+ FloatVectorOperations::subtract (data1, data2, num);
+ u.expect (areAllValuesEqual (data1, num, (ValueType) 512));
+
+ fillRandomly (random, int1, num);
+ doConversionTest (u, data1, data2, int1, num);
+ }
+
+ static void doConversionTest (UnitTest& u, float* data1, float* data2, int* const int1, int num)
+ {
+ FloatVectorOperations::convertFixedToFloat (data1, int1, 2.0f, num);
+ convertFixed (data2, int1, 2.0f, num);
+ u.expect (buffersMatch (data1, data2, num));
+ }
+
+ static void doConversionTest (UnitTest&, double*, double*, int*, int) {}
+
+ static void fillRandomly (Random& random, ValueType* d, int num)
+ {
+ while (--num >= 0)
+ *d++ = (ValueType) (random.nextDouble() * 1000.0);
+ }
+
+ static void fillRandomly (Random& random, int* d, int num)
+ {
+ while (--num >= 0)
+ *d++ = random.nextInt();
+ }
+
+ static void convertFixed (float* d, const int* s, ValueType multiplier, int num)
+ {
+ while (--num >= 0)
+ *d++ = *s++ * multiplier;
+ }
+
+ static bool areAllValuesEqual (const ValueType* d, int num, ValueType target)
+ {
+ while (--num >= 0)
+ if (*d++ != target)
+ return false;
+
+ return true;
+ }
+
+ static bool buffersMatch (const ValueType* d1, const ValueType* d2, int num)
+ {
+ while (--num >= 0)
+ if (! valuesMatch (*d1++, *d2++))
+ return false;
+
+ return true;
+ }
+
+ static bool valuesMatch (ValueType v1, ValueType v2)
+ {
+ return std::abs (v1 - v2) < std::numeric_limits::epsilon();
+ }
+ };
+
+ void runTest()
+ {
+ beginTest ("FloatVectorOperations");
+
+ for (int i = 1000; --i >= 0;)
+ {
+ TestRunner::runTest (*this, getRandom());
+ TestRunner::runTest (*this, getRandom());
+ }
+ }
+};
+
+static FloatVectorOperationsTests vectorOpTests;
+
+#endif
diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h
new file mode 100644
index 0000000..0b3fcb6
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h
@@ -0,0 +1,162 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_FLOATVECTOROPERATIONS_H_INCLUDED
+#define JUCE_FLOATVECTOROPERATIONS_H_INCLUDED
+
+
+//==============================================================================
+/**
+ A collection of simple vector operations on arrays of floats, accelerated with
+ SIMD instructions where possible.
+*/
+class JUCE_API FloatVectorOperations
+{
+public:
+ //==============================================================================
+ /** Clears a vector of floats. */
+ static void JUCE_CALLTYPE clear (float* dest, int numValues) noexcept;
+
+ /** Clears a vector of doubles. */
+ static void JUCE_CALLTYPE clear (double* dest, int numValues) noexcept;
+
+ /** Copies a repeated value into a vector of floats. */
+ static void JUCE_CALLTYPE fill (float* dest, float valueToFill, int numValues) noexcept;
+
+ /** Copies a repeated value into a vector of doubles. */
+ static void JUCE_CALLTYPE fill (double* dest, double valueToFill, int numValues) noexcept;
+
+ /** Copies a vector of floats. */
+ static void JUCE_CALLTYPE copy (float* dest, const float* src, int numValues) noexcept;
+
+ /** Copies a vector of doubles. */
+ static void JUCE_CALLTYPE copy (double* dest, const double* src, int numValues) noexcept;
+
+ /** Copies a vector of floats, multiplying each value by a given multiplier */
+ static void JUCE_CALLTYPE copyWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept;
+
+ /** Copies a vector of doubles, multiplying each value by a given multiplier */
+ static void JUCE_CALLTYPE copyWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept;
+
+ /** Adds a fixed value to the destination values. */
+ static void JUCE_CALLTYPE add (float* dest, float amountToAdd, int numValues) noexcept;
+
+ /** Adds a fixed value to the destination values. */
+ 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. */
+ static void JUCE_CALLTYPE add (float* dest, const float* src, int numValues) noexcept;
+
+ /** Adds the source values to the destination values. */
+ 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. */
+ static void JUCE_CALLTYPE subtract (float* dest, const float* src, int numValues) noexcept;
+
+ /** Subtracts the source values from the destination values. */
+ 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. */
+ static void JUCE_CALLTYPE addWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept;
+
+ /** Multiplies each source value by the given multiplier, then adds it to the destination value. */
+ static void JUCE_CALLTYPE addWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept;
+
+ /** Multiplies the destination values by the source values. */
+ static void JUCE_CALLTYPE multiply (float* dest, const float* src, int numValues) noexcept;
+
+ /** Multiplies the destination values by the source values. */
+ 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. */
+ static void JUCE_CALLTYPE multiply (float* dest, float multiplier, int numValues) noexcept;
+
+ /** Multiplies each of the destination values by a fixed multiplier. */
+ 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. */
+ static void JUCE_CALLTYPE negate (float* dest, const float* src, int numValues) noexcept;
+
+ /** Copies a source vector to a destination, negating each value. */
+ static void JUCE_CALLTYPE negate (double* dest, const double* src, int numValues) noexcept;
+
+ /** Converts a stream of integers to floats, multiplying each one by the given multiplier. */
+ static void JUCE_CALLTYPE convertFixedToFloat (float* dest, const int* src, float multiplier, int numValues) noexcept;
+
+ /** Finds the miniumum and maximum values in the given array. */
+ static Range JUCE_CALLTYPE findMinAndMax (const float* src, int numValues) noexcept;
+
+ /** Finds the miniumum and maximum values in the given array. */
+ static Range JUCE_CALLTYPE findMinAndMax (const double* src, int numValues) noexcept;
+
+ /** Finds the miniumum value in the given array. */
+ static float JUCE_CALLTYPE findMinimum (const float* src, int numValues) noexcept;
+
+ /** Finds the miniumum value in the given array. */
+ static double JUCE_CALLTYPE findMinimum (const double* src, int numValues) noexcept;
+
+ /** Finds the maximum value in the given array. */
+ static float JUCE_CALLTYPE findMaximum (const float* src, int numValues) noexcept;
+
+ /** Finds the maximum value in the given array. */
+ static double JUCE_CALLTYPE findMaximum (const double* src, int numValues) noexcept;
+
+ /** On Intel CPUs, this method enables or disables the SSE flush-to-zero mode.
+ Effectively, this is a wrapper around a call to _MM_SET_FLUSH_ZERO_MODE
+ */
+ static void JUCE_CALLTYPE enableFlushToZeroMode (bool shouldEnable) noexcept;
+};
+
+
+#endif // JUCE_FLOATVECTOROPERATIONS_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/effects/juce_Decibels.h b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_Decibels.h
new file mode 100644
index 0000000..c63746c
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_Decibels.h
@@ -0,0 +1,104 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_DECIBELS_H_INCLUDED
+#define JUCE_DECIBELS_H_INCLUDED
+
+
+//==============================================================================
+/**
+ This class contains some helpful static methods for dealing with decibel values.
+*/
+class Decibels
+{
+public:
+ //==============================================================================
+ /** Converts a dBFS value to its equivalent gain level.
+
+ A gain of 1.0 = 0 dB, and lower gains map onto negative decibel values. Any
+ decibel value lower than minusInfinityDb will return a gain of 0.
+ */
+ template
+ static Type decibelsToGain (const Type decibels,
+ const Type minusInfinityDb = (Type) defaultMinusInfinitydB)
+ {
+ return decibels > minusInfinityDb ? std::pow ((Type) 10.0, decibels * (Type) 0.05)
+ : Type();
+ }
+
+ /** Converts a gain level into a dBFS value.
+
+ A gain of 1.0 = 0 dB, and lower gains map onto negative decibel values.
+ If the gain is 0 (or negative), then the method will return the value
+ provided as minusInfinityDb.
+ */
+ template
+ static Type gainToDecibels (const Type gain,
+ const Type minusInfinityDb = (Type) defaultMinusInfinitydB)
+ {
+ return gain > Type() ? jmax (minusInfinityDb, (Type) std::log10 (gain) * (Type) 20.0)
+ : minusInfinityDb;
+ }
+
+ //==============================================================================
+ /** Converts a decibel reading to a string, with the 'dB' suffix.
+ If the decibel value is lower than minusInfinityDb, the return value will
+ be "-INF dB".
+ */
+ template
+ static String toString (const Type decibels,
+ const int decimalPlaces = 2,
+ const Type minusInfinityDb = (Type) defaultMinusInfinitydB)
+ {
+ String s;
+
+ if (decibels <= minusInfinityDb)
+ {
+ s = "-INF dB";
+ }
+ else
+ {
+ if (decibels >= Type())
+ s << '+';
+
+ s << String (decibels, decimalPlaces) << " dB";
+ }
+
+ return s;
+ }
+
+
+private:
+ //==============================================================================
+ enum
+ {
+ defaultMinusInfinitydB = -100
+ };
+
+ Decibels(); // This class can't be instantiated, it's just a holder for static methods..
+ JUCE_DECLARE_NON_COPYABLE (Decibels)
+};
+
+
+#endif // JUCE_DECIBELS_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/effects/juce_IIRFilter.cpp b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_IIRFilter.cpp
new file mode 100644
index 0000000..b961a6a
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_IIRFilter.cpp
@@ -0,0 +1,244 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#if JUCE_INTEL
+ #define JUCE_SNAP_TO_ZERO(n) if (! (n < -1.0e-8f || n > 1.0e-8f)) n = 0;
+#else
+ #define JUCE_SNAP_TO_ZERO(n)
+#endif
+
+//==============================================================================
+IIRCoefficients::IIRCoefficients() noexcept
+{
+ zeromem (coefficients, sizeof (coefficients));
+}
+
+IIRCoefficients::~IIRCoefficients() noexcept {}
+
+IIRCoefficients::IIRCoefficients (const IIRCoefficients& other) noexcept
+{
+ memcpy (coefficients, other.coefficients, sizeof (coefficients));
+}
+
+IIRCoefficients& IIRCoefficients::operator= (const IIRCoefficients& other) noexcept
+{
+ memcpy (coefficients, other.coefficients, sizeof (coefficients));
+ return *this;
+}
+
+IIRCoefficients::IIRCoefficients (double c1, double c2, double c3,
+ double c4, double c5, double c6) noexcept
+{
+ const double a = 1.0 / c4;
+
+ coefficients[0] = (float) (c1 * a);
+ coefficients[1] = (float) (c2 * a);
+ coefficients[2] = (float) (c3 * a);
+ coefficients[3] = (float) (c5 * a);
+ coefficients[4] = (float) (c6 * a);
+}
+
+IIRCoefficients IIRCoefficients::makeLowPass (const double sampleRate,
+ const double frequency) noexcept
+{
+ jassert (sampleRate > 0);
+
+ const double n = 1.0 / std::tan (double_Pi * frequency / sampleRate);
+ const double nSquared = n * n;
+ const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared);
+
+ return IIRCoefficients (c1,
+ c1 * 2.0,
+ c1,
+ 1.0,
+ c1 * 2.0 * (1.0 - nSquared),
+ c1 * (1.0 - std::sqrt (2.0) * n + nSquared));
+}
+
+IIRCoefficients IIRCoefficients::makeHighPass (const double sampleRate,
+ const double frequency) noexcept
+{
+ const double n = std::tan (double_Pi * frequency / sampleRate);
+ const double nSquared = n * n;
+ const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared);
+
+ return IIRCoefficients (c1,
+ c1 * -2.0,
+ c1,
+ 1.0,
+ c1 * 2.0 * (nSquared - 1.0),
+ c1 * (1.0 - std::sqrt (2.0) * n + nSquared));
+}
+
+IIRCoefficients IIRCoefficients::makeLowShelf (const double sampleRate,
+ const double cutOffFrequency,
+ const double Q,
+ const float gainFactor) noexcept
+{
+ jassert (sampleRate > 0);
+ jassert (Q > 0);
+
+ const double A = jmax (0.0f, std::sqrt (gainFactor));
+ const double aminus1 = A - 1.0;
+ const double aplus1 = A + 1.0;
+ const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate;
+ const double coso = std::cos (omega);
+ const double beta = std::sin (omega) * std::sqrt (A) / Q;
+ const double aminus1TimesCoso = aminus1 * coso;
+
+ return IIRCoefficients (A * (aplus1 - aminus1TimesCoso + beta),
+ A * 2.0 * (aminus1 - aplus1 * coso),
+ A * (aplus1 - aminus1TimesCoso - beta),
+ aplus1 + aminus1TimesCoso + beta,
+ -2.0 * (aminus1 + aplus1 * coso),
+ aplus1 + aminus1TimesCoso - beta);
+}
+
+IIRCoefficients IIRCoefficients::makeHighShelf (const double sampleRate,
+ const double cutOffFrequency,
+ const double Q,
+ const float gainFactor) noexcept
+{
+ jassert (sampleRate > 0);
+ jassert (Q > 0);
+
+ const double A = jmax (0.0f, std::sqrt (gainFactor));
+ const double aminus1 = A - 1.0;
+ const double aplus1 = A + 1.0;
+ const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate;
+ const double coso = std::cos (omega);
+ const double beta = std::sin (omega) * std::sqrt (A) / Q;
+ const double aminus1TimesCoso = aminus1 * coso;
+
+ return IIRCoefficients (A * (aplus1 + aminus1TimesCoso + beta),
+ A * -2.0 * (aminus1 + aplus1 * coso),
+ A * (aplus1 + aminus1TimesCoso - beta),
+ aplus1 - aminus1TimesCoso + beta,
+ 2.0 * (aminus1 - aplus1 * coso),
+ aplus1 - aminus1TimesCoso - beta);
+}
+
+IIRCoefficients IIRCoefficients::makePeakFilter (const double sampleRate,
+ const double centreFrequency,
+ const double Q,
+ const float gainFactor) noexcept
+{
+ jassert (sampleRate > 0);
+ jassert (Q > 0);
+
+ const double A = jmax (0.0f, std::sqrt (gainFactor));
+ const double omega = (double_Pi * 2.0 * jmax (centreFrequency, 2.0)) / sampleRate;
+ const double alpha = 0.5 * std::sin (omega) / Q;
+ const double c2 = -2.0 * std::cos (omega);
+ const double alphaTimesA = alpha * A;
+ const double alphaOverA = alpha / A;
+
+ return IIRCoefficients (1.0 + alphaTimesA,
+ c2,
+ 1.0 - alphaTimesA,
+ 1.0 + alphaOverA,
+ c2,
+ 1.0 - alphaOverA);
+}
+
+//==============================================================================
+IIRFilter::IIRFilter() noexcept
+ : v1 (0), v2 (0), active (false)
+{
+}
+
+IIRFilter::IIRFilter (const IIRFilter& other) noexcept
+ : v1 (0), v2 (0), active (other.active)
+{
+ const SpinLock::ScopedLockType sl (other.processLock);
+ coefficients = other.coefficients;
+}
+
+IIRFilter::~IIRFilter() noexcept
+{
+}
+
+//==============================================================================
+void IIRFilter::makeInactive() noexcept
+{
+ const SpinLock::ScopedLockType sl (processLock);
+ active = false;
+}
+
+void IIRFilter::setCoefficients (const IIRCoefficients& newCoefficients) noexcept
+{
+ const SpinLock::ScopedLockType sl (processLock);
+
+ coefficients = newCoefficients;
+ active = true;
+}
+
+//==============================================================================
+void IIRFilter::reset() noexcept
+{
+ const SpinLock::ScopedLockType sl (processLock);
+ v1 = v2 = 0;
+}
+
+float IIRFilter::processSingleSampleRaw (const float in) noexcept
+{
+ float out = coefficients.coefficients[0] * in + v1;
+
+ JUCE_SNAP_TO_ZERO (out);
+
+ v1 = coefficients.coefficients[1] * in - coefficients.coefficients[3] * out + v2;
+ v2 = coefficients.coefficients[2] * in - coefficients.coefficients[4] * out;
+
+ return out;
+}
+
+void IIRFilter::processSamples (float* const samples, const int numSamples) noexcept
+{
+ const SpinLock::ScopedLockType sl (processLock);
+
+ if (active)
+ {
+ const float c0 = coefficients.coefficients[0];
+ const float c1 = coefficients.coefficients[1];
+ const float c2 = coefficients.coefficients[2];
+ const float c3 = coefficients.coefficients[3];
+ const float c4 = coefficients.coefficients[4];
+ float lv1 = v1, lv2 = v2;
+
+ for (int i = 0; i < numSamples; ++i)
+ {
+ const float in = samples[i];
+ const float out = c0 * in + lv1;
+ samples[i] = out;
+
+ lv1 = c1 * in - c3 * out + lv2;
+ lv2 = c2 * in - c4 * out;
+ }
+
+ JUCE_SNAP_TO_ZERO (lv1); v1 = lv1;
+ JUCE_SNAP_TO_ZERO (lv2); v2 = lv2;
+ }
+}
+
+#undef JUCE_SNAP_TO_ZERO
diff --git a/JuceLibraryCode/modules/juce_audio_basics/effects/juce_IIRFilter.h b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_IIRFilter.h
new file mode 100644
index 0000000..8269cf5
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_IIRFilter.h
@@ -0,0 +1,174 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_IIRFILTER_H_INCLUDED
+#define JUCE_IIRFILTER_H_INCLUDED
+
+class IIRFilter;
+
+//==============================================================================
+/**
+ A set of coefficients for use in an IIRFilter object.
+
+ @see IIRFilter
+*/
+class JUCE_API IIRCoefficients
+{
+public:
+ //==============================================================================
+ /** Creates a null set of coefficients (which will produce silence). */
+ IIRCoefficients() noexcept;
+
+ /** Directly constructs an object from the raw coefficients.
+ Most people will want to use the static methods instead of this, but
+ the constructor is public to allow tinkerers to create their own custom
+ filters!
+ */
+ IIRCoefficients (double c1, double c2, double c3,
+ double c4, double c5, double c6) noexcept;
+
+ /** Creates a copy of another filter. */
+ IIRCoefficients (const IIRCoefficients&) noexcept;
+ /** Creates a copy of another filter. */
+ IIRCoefficients& operator= (const IIRCoefficients&) noexcept;
+ /** Destructor. */
+ ~IIRCoefficients() noexcept;
+
+ /** Returns the coefficients for a low-pass filter. */
+ static IIRCoefficients makeLowPass (double sampleRate,
+ double frequency) noexcept;
+
+ /** Returns the coefficients for a high-pass filter. */
+ static IIRCoefficients makeHighPass (double sampleRate,
+ double frequency) noexcept;
+
+ //==============================================================================
+ /** Returns the coefficients for a low-pass shelf filter with variable Q and gain.
+
+ The gain is a scale factor that the low frequencies are multiplied by, so values
+ greater than 1.0 will boost the low frequencies, values less than 1.0 will
+ attenuate them.
+ */
+ static IIRCoefficients makeLowShelf (double sampleRate,
+ double cutOffFrequency,
+ double Q,
+ float gainFactor) noexcept;
+
+ /** Returns the coefficients for a high-pass shelf filter with variable Q and gain.
+
+ The gain is a scale factor that the high frequencies are multiplied by, so values
+ greater than 1.0 will boost the high frequencies, values less than 1.0 will
+ attenuate them.
+ */
+ static IIRCoefficients makeHighShelf (double sampleRate,
+ double cutOffFrequency,
+ double Q,
+ float gainFactor) noexcept;
+
+ /** Returns the coefficients for a peak filter centred around a
+ given frequency, with a variable Q and gain.
+
+ The gain is a scale factor that the centre frequencies are multiplied by, so
+ values greater than 1.0 will boost the centre frequencies, values less than
+ 1.0 will attenuate them.
+ */
+ static IIRCoefficients makePeakFilter (double sampleRate,
+ double centreFrequency,
+ double Q,
+ float gainFactor) noexcept;
+
+ //==============================================================================
+ /** The raw coefficients.
+ You should leave these numbers alone unless you really know what you're doing.
+ */
+ float coefficients[5];
+};
+
+//==============================================================================
+/**
+ An IIR filter that can perform low, high, or band-pass filtering on an
+ audio signal.
+
+ @see IIRCoefficient, IIRFilterAudioSource
+*/
+class JUCE_API IIRFilter
+{
+public:
+ //==============================================================================
+ /** Creates a filter.
+
+ Initially the filter is inactive, so will have no effect on samples that
+ you process with it. Use the setCoefficients() method to turn it into the
+ type of filter needed.
+ */
+ IIRFilter() noexcept;
+
+ /** Creates a copy of another filter. */
+ IIRFilter (const IIRFilter&) noexcept;
+
+ /** Destructor. */
+ ~IIRFilter() noexcept;
+
+ //==============================================================================
+ /** Clears the filter so that any incoming data passes through unchanged. */
+ void makeInactive() noexcept;
+
+ /** Applies a set of coefficients to this filter. */
+ void setCoefficients (const IIRCoefficients& newCoefficients) noexcept;
+
+ /** Returns the coefficients that this filter is using. */
+ IIRCoefficients getCoefficients() const noexcept { return coefficients; }
+
+ //==============================================================================
+ /** Resets the filter's processing pipeline, ready to start a new stream of data.
+
+ Note that this clears the processing state, but the type of filter and
+ its coefficients aren't changed. To put a filter into an inactive state, use
+ the makeInactive() method.
+ */
+ void reset() noexcept;
+
+ /** Performs the filter operation on the given set of samples. */
+ void processSamples (float* samples, int numSamples) noexcept;
+
+ /** Processes a single sample, without any locking or checking.
+
+ Use this if you need fast processing of a single value, but be aware that
+ this isn't thread-safe in the way that processSamples() is.
+ */
+ float processSingleSampleRaw (float sample) noexcept;
+
+protected:
+ //==============================================================================
+ SpinLock processLock;
+ IIRCoefficients coefficients;
+ float v1, v2;
+ bool active;
+
+ IIRFilter& operator= (const IIRFilter&);
+ JUCE_LEAK_DETECTOR (IIRFilter)
+};
+
+
+#endif // JUCE_IIRFILTER_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.cpp b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.cpp
new file mode 100644
index 0000000..7bab3a5
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.cpp
@@ -0,0 +1,200 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+namespace LagrangeHelpers
+{
+ template
+ struct ResampleHelper
+ {
+ static forcedinline void calc (float& a, float b) { a *= b * (1.0f / k); }
+ };
+
+ template<>
+ struct ResampleHelper <0>
+ {
+ static forcedinline void calc (float&, float) {}
+ };
+
+ template
+ static forcedinline float calcCoefficient (float input, const float offset) noexcept
+ {
+ ResampleHelper <0 - k>::calc (input, -2.0f - offset);
+ ResampleHelper <1 - k>::calc (input, -1.0f - offset);
+ ResampleHelper <2 - k>::calc (input, 0.0f - offset);
+ ResampleHelper <3 - k>::calc (input, 1.0f - offset);
+ ResampleHelper <4 - k>::calc (input, 2.0f - offset);
+ return input;
+ }
+
+ static forcedinline float valueAtOffset (const float* const inputs, const float offset) noexcept
+ {
+ return calcCoefficient<0> (inputs[4], offset)
+ + calcCoefficient<1> (inputs[3], offset)
+ + calcCoefficient<2> (inputs[2], offset)
+ + calcCoefficient<3> (inputs[1], offset)
+ + calcCoefficient<4> (inputs[0], offset);
+ }
+
+ static forcedinline void push (float* inputs, const float newValue) noexcept
+ {
+ inputs[4] = inputs[3];
+ inputs[3] = inputs[2];
+ inputs[2] = inputs[1];
+ inputs[1] = inputs[0];
+ inputs[0] = newValue;
+ }
+}
+
+//==============================================================================
+LagrangeInterpolator::LagrangeInterpolator() { reset(); }
+LagrangeInterpolator::~LagrangeInterpolator() {}
+
+void LagrangeInterpolator::reset() noexcept
+{
+ subSamplePos = 1.0;
+
+ for (int i = 0; i < numElementsInArray (lastInputSamples); ++i)
+ lastInputSamples[i] = 0;
+}
+
+int LagrangeInterpolator::process (const double actualRatio, const float* in,
+ float* out, const int numOut) noexcept
+{
+ if (actualRatio == 1.0)
+ {
+ memcpy (out, in, (size_t) numOut * sizeof (float));
+
+ if (numOut >= 4)
+ {
+ memcpy (lastInputSamples, in + (numOut - 4), 4 * sizeof (float));
+ }
+ else
+ {
+ for (int i = 0; i < numOut; ++i)
+ LagrangeHelpers::push (lastInputSamples, in[i]);
+ }
+
+ return numOut;
+ }
+
+ const float* const originalIn = in;
+ double pos = subSamplePos;
+
+ if (actualRatio < 1.0)
+ {
+ for (int i = numOut; --i >= 0;)
+ {
+ if (pos >= 1.0)
+ {
+ LagrangeHelpers::push (lastInputSamples, *in++);
+ pos -= 1.0;
+ }
+
+ *out++ = LagrangeHelpers::valueAtOffset (lastInputSamples, (float) pos);
+ pos += actualRatio;
+ }
+ }
+ else
+ {
+ for (int i = numOut; --i >= 0;)
+ {
+ while (pos < actualRatio)
+ {
+ LagrangeHelpers::push (lastInputSamples, *in++);
+ pos += 1.0;
+ }
+
+ pos -= actualRatio;
+ *out++ = LagrangeHelpers::valueAtOffset (lastInputSamples, 1.0f - (float) pos);
+ }
+ }
+
+ subSamplePos = pos;
+ return (int) (in - originalIn);
+}
+
+int LagrangeInterpolator::processAdding (const double actualRatio, const float* in,
+ float* out, const int numOut, const float gain) noexcept
+{
+ if (actualRatio == 1.0)
+ {
+ if (gain != 1.0f)
+ {
+ for (int i = 0; i < numOut; ++i)
+ out[i] += in[i] * gain;
+ }
+ else
+ {
+ for (int i = 0; i < numOut; ++i)
+ out[i] += in[i];
+ }
+
+ if (numOut >= 4)
+ {
+ memcpy (lastInputSamples, in + (numOut - 4), 4 * sizeof (float));
+ }
+ else
+ {
+ for (int i = 0; i < numOut; ++i)
+ LagrangeHelpers::push (lastInputSamples, in[i]);
+ }
+
+ return numOut;
+ }
+
+ const float* const originalIn = in;
+ double pos = subSamplePos;
+
+ if (actualRatio < 1.0)
+ {
+ for (int i = numOut; --i >= 0;)
+ {
+ if (pos >= 1.0)
+ {
+ LagrangeHelpers::push (lastInputSamples, *in++);
+ pos -= 1.0;
+ }
+
+ *out++ += gain * LagrangeHelpers::valueAtOffset (lastInputSamples, (float) pos);
+ pos += actualRatio;
+ }
+ }
+ else
+ {
+ for (int i = numOut; --i >= 0;)
+ {
+ while (pos < actualRatio)
+ {
+ LagrangeHelpers::push (lastInputSamples, *in++);
+ pos += 1.0;
+ }
+
+ pos -= actualRatio;
+ *out++ += gain * LagrangeHelpers::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos));
+ }
+ }
+
+ subSamplePos = pos;
+ return (int) (in - originalIn);
+}
diff --git a/JuceLibraryCode/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.h b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.h
new file mode 100644
index 0000000..caa4802
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.h
@@ -0,0 +1,94 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_LAGRANGEINTERPOLATOR_H_INCLUDED
+#define JUCE_LAGRANGEINTERPOLATOR_H_INCLUDED
+
+
+//==============================================================================
+/**
+ Interpolator for resampling a stream of floats using 4-point lagrange interpolation.
+
+ Note that the resampler is stateful, so when there's a break in the continuity
+ of the input stream you're feeding it, you should call reset() before feeding
+ it any new data. And like with any other stateful filter, if you're resampling
+ multiple channels, make sure each one uses its own LagrangeInterpolator
+ object.
+*/
+class JUCE_API LagrangeInterpolator
+{
+public:
+ LagrangeInterpolator();
+ ~LagrangeInterpolator();
+
+ /** Resets the state of the interpolator.
+ Call this when there's a break in the continuity of the input data stream.
+ */
+ void reset() noexcept;
+
+ /** Resamples a stream of samples.
+
+ @param speedRatio the number of input samples to use for each output sample
+ @param inputSamples the source data to read from. This must contain at
+ least (speedRatio * numOutputSamplesToProduce) samples.
+ @param outputSamples the buffer to write the results into
+ @param numOutputSamplesToProduce the number of output samples that should be created
+
+ @returns the actual number of input samples that were used
+ */
+ int process (double speedRatio,
+ const float* inputSamples,
+ float* outputSamples,
+ int numOutputSamplesToProduce) noexcept;
+
+ /** Resamples a stream of samples, adding the results to the output data
+ with a gain.
+
+ @param speedRatio the number of input samples to use for each output sample
+ @param inputSamples the source data to read from. This must contain at
+ least (speedRatio * numOutputSamplesToProduce) samples.
+ @param outputSamples the buffer to write the results to - the result values will be added
+ to any pre-existing data in this buffer after being multiplied by
+ the gain factor
+ @param numOutputSamplesToProduce the number of output samples that should be created
+ @param gain a gain factor to multiply the resulting samples by before
+ adding them to the destination buffer
+
+ @returns the actual number of input samples that were used
+ */
+ int processAdding (double speedRatio,
+ const float* inputSamples,
+ float* outputSamples,
+ int numOutputSamplesToProduce,
+ float gain) noexcept;
+
+private:
+ float lastInputSamples[5];
+ double subSamplePos;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LagrangeInterpolator)
+};
+
+
+#endif // JUCE_LAGRANGEINTERPOLATOR_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/effects/juce_Reverb.h b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_Reverb.h
new file mode 100644
index 0000000..c0b7596
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_Reverb.h
@@ -0,0 +1,372 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_REVERB_H_INCLUDED
+#define JUCE_REVERB_H_INCLUDED
+
+
+//==============================================================================
+/**
+ Performs a simple reverb effect on a stream of audio data.
+
+ This is a simple stereo reverb, based on the technique and tunings used in FreeVerb.
+ Use setSampleRate() to prepare it, and then call processStereo() or processMono() to
+ apply the reverb to your audio data.
+
+ @see ReverbAudioSource
+*/
+class Reverb
+{
+public:
+ //==============================================================================
+ Reverb()
+ {
+ setParameters (Parameters());
+ setSampleRate (44100.0);
+ }
+
+ //==============================================================================
+ /** Holds the parameters being used by a Reverb object. */
+ struct Parameters
+ {
+ Parameters() noexcept
+ : roomSize (0.5f),
+ damping (0.5f),
+ wetLevel (0.33f),
+ dryLevel (0.4f),
+ width (1.0f),
+ freezeMode (0)
+ {}
+
+ float roomSize; /**< Room size, 0 to 1.0, where 1.0 is big, 0 is small. */
+ float damping; /**< Damping, 0 to 1.0, where 0 is not damped, 1.0 is fully damped. */
+ float wetLevel; /**< Wet level, 0 to 1.0 */
+ float dryLevel; /**< Dry level, 0 to 1.0 */
+ float width; /**< Reverb width, 0 to 1.0, where 1.0 is very wide. */
+ float freezeMode; /**< Freeze mode - values < 0.5 are "normal" mode, values > 0.5
+ put the reverb into a continuous feedback loop. */
+ };
+
+ //==============================================================================
+ /** Returns the reverb's current parameters. */
+ const Parameters& getParameters() const noexcept { return parameters; }
+
+ /** Applies a new set of parameters to the reverb.
+ Note that this doesn't attempt to lock the reverb, so if you call this in parallel with
+ the process method, you may get artifacts.
+ */
+ void setParameters (const Parameters& newParams)
+ {
+ const float wetScaleFactor = 3.0f;
+ const float dryScaleFactor = 2.0f;
+
+ const float wet = newParams.wetLevel * wetScaleFactor;
+ dryGain.setValue (newParams.dryLevel * dryScaleFactor);
+ wetGain1.setValue (0.5f * wet * (1.0f + newParams.width));
+ wetGain2.setValue (0.5f * wet * (1.0f - newParams.width));
+
+ gain = isFrozen (newParams.freezeMode) ? 0.0f : 0.015f;
+ parameters = newParams;
+ updateDamping();
+ }
+
+ //==============================================================================
+ /** Sets the sample rate that will be used for the reverb.
+ You must call this before the process methods, in order to tell it the correct sample rate.
+ */
+ void setSampleRate (const double sampleRate)
+ {
+ jassert (sampleRate > 0);
+
+ static const short combTunings[] = { 1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617 }; // (at 44100Hz)
+ static const short allPassTunings[] = { 556, 441, 341, 225 };
+ const int stereoSpread = 23;
+ const int intSampleRate = (int) sampleRate;
+
+ for (int i = 0; i < numCombs; ++i)
+ {
+ comb[0][i].setSize ((intSampleRate * combTunings[i]) / 44100);
+ comb[1][i].setSize ((intSampleRate * (combTunings[i] + stereoSpread)) / 44100);
+ }
+
+ for (int i = 0; i < numAllPasses; ++i)
+ {
+ allPass[0][i].setSize ((intSampleRate * allPassTunings[i]) / 44100);
+ allPass[1][i].setSize ((intSampleRate * (allPassTunings[i] + stereoSpread)) / 44100);
+ }
+
+ const double smoothTime = 0.01;
+ damping .reset (sampleRate, smoothTime);
+ feedback.reset (sampleRate, smoothTime);
+ dryGain .reset (sampleRate, smoothTime);
+ wetGain1.reset (sampleRate, smoothTime);
+ wetGain2.reset (sampleRate, smoothTime);
+ }
+
+ /** Clears the reverb's buffers. */
+ void reset()
+ {
+ for (int j = 0; j < numChannels; ++j)
+ {
+ for (int i = 0; i < numCombs; ++i)
+ comb[j][i].clear();
+
+ for (int i = 0; i < numAllPasses; ++i)
+ allPass[j][i].clear();
+ }
+ }
+
+ //==============================================================================
+ /** Applies the reverb to two stereo channels of audio data. */
+ void processStereo (float* const left, float* const right, const int numSamples) noexcept
+ {
+ jassert (left != nullptr && right != nullptr);
+
+ for (int i = 0; i < numSamples; ++i)
+ {
+ const float input = (left[i] + right[i]) * gain;
+ float outL = 0, outR = 0;
+
+ const float damp = damping.getNextValue();
+ const float feedbck = feedback.getNextValue();
+
+ for (int j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel
+ {
+ outL += comb[0][j].process (input, damp, feedbck);
+ outR += comb[1][j].process (input, damp, feedbck);
+ }
+
+ for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series
+ {
+ outL = allPass[0][j].process (outL);
+ outR = allPass[1][j].process (outR);
+ }
+
+ const float dry = dryGain.getNextValue();
+ const float wet1 = wetGain1.getNextValue();
+ const float wet2 = wetGain2.getNextValue();
+
+ left[i] = outL * wet1 + outR * wet2 + left[i] * dry;
+ right[i] = outR * wet1 + outL * wet2 + right[i] * dry;
+ }
+ }
+
+ /** Applies the reverb to a single mono channel of audio data. */
+ void processMono (float* const samples, const int numSamples) noexcept
+ {
+ jassert (samples != nullptr);
+
+ for (int i = 0; i < numSamples; ++i)
+ {
+ const float input = samples[i] * gain;
+ float output = 0;
+
+ const float damp = damping.getNextValue();
+ const float feedbck = feedback.getNextValue();
+
+ for (int j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel
+ output += comb[0][j].process (input, damp, feedbck);
+
+ for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series
+ output = allPass[0][j].process (output);
+
+ const float dry = dryGain.getNextValue();
+ const float wet1 = wetGain1.getNextValue();
+
+ samples[i] = output * wet1 + samples[i] * dry;
+ }
+ }
+
+private:
+ //==============================================================================
+ static bool isFrozen (const float freezeMode) noexcept { return freezeMode >= 0.5f; }
+
+ void updateDamping() noexcept
+ {
+ const float roomScaleFactor = 0.28f;
+ const float roomOffset = 0.7f;
+ const float dampScaleFactor = 0.4f;
+
+ if (isFrozen (parameters.freezeMode))
+ setDamping (0.0f, 1.0f);
+ else
+ setDamping (parameters.damping * dampScaleFactor,
+ parameters.roomSize * roomScaleFactor + roomOffset);
+ }
+
+ void setDamping (const float dampingToUse, const float roomSizeToUse) noexcept
+ {
+ damping.setValue (dampingToUse);
+ feedback.setValue (roomSizeToUse);
+ }
+
+ //==============================================================================
+ class CombFilter
+ {
+ public:
+ CombFilter() noexcept : bufferSize (0), bufferIndex (0), last (0) {}
+
+ void setSize (const int size)
+ {
+ if (size != bufferSize)
+ {
+ bufferIndex = 0;
+ buffer.malloc ((size_t) size);
+ bufferSize = size;
+ }
+
+ clear();
+ }
+
+ void clear() noexcept
+ {
+ last = 0;
+ buffer.clear ((size_t) bufferSize);
+ }
+
+ float process (const float input, const float damp, const float feedbackLevel) noexcept
+ {
+ const float output = buffer[bufferIndex];
+ last = (output * (1.0f - damp)) + (last * damp);
+ JUCE_UNDENORMALISE (last);
+
+ float temp = input + (last * feedbackLevel);
+ JUCE_UNDENORMALISE (temp);
+ buffer[bufferIndex] = temp;
+ bufferIndex = (bufferIndex + 1) % bufferSize;
+ return output;
+ }
+
+ private:
+ HeapBlock buffer;
+ int bufferSize, bufferIndex;
+ float last;
+
+ JUCE_DECLARE_NON_COPYABLE (CombFilter)
+ };
+
+ //==============================================================================
+ class AllPassFilter
+ {
+ public:
+ AllPassFilter() noexcept : bufferSize (0), bufferIndex (0) {}
+
+ void setSize (const int size)
+ {
+ if (size != bufferSize)
+ {
+ bufferIndex = 0;
+ buffer.malloc ((size_t) size);
+ bufferSize = size;
+ }
+
+ clear();
+ }
+
+ void clear() noexcept
+ {
+ buffer.clear ((size_t) bufferSize);
+ }
+
+ float process (const float input) noexcept
+ {
+ const float bufferedValue = buffer [bufferIndex];
+ float temp = input + (bufferedValue * 0.5f);
+ JUCE_UNDENORMALISE (temp);
+ buffer [bufferIndex] = temp;
+ bufferIndex = (bufferIndex + 1) % bufferSize;
+ return bufferedValue - input;
+ }
+
+ private:
+ HeapBlock buffer;
+ int bufferSize, bufferIndex;
+
+ JUCE_DECLARE_NON_COPYABLE (AllPassFilter)
+ };
+
+ //==============================================================================
+ class LinearSmoothedValue
+ {
+ public:
+ LinearSmoothedValue() noexcept
+ : currentValue (0), target (0), step (0), countdown (0), stepsToTarget (0)
+ {
+ }
+
+ void reset (double sampleRate, double fadeLengthSeconds) noexcept
+ {
+ jassert (sampleRate > 0 && fadeLengthSeconds >= 0);
+ stepsToTarget = (int) std::floor (fadeLengthSeconds * sampleRate);
+ currentValue = target;
+ countdown = 0;
+ }
+
+ void setValue (float newValue) noexcept
+ {
+ if (target != newValue)
+ {
+ target = newValue;
+ countdown = stepsToTarget;
+
+ if (countdown <= 0)
+ currentValue = target;
+ else
+ step = (target - currentValue) / countdown;
+ }
+ }
+
+ float getNextValue() noexcept
+ {
+ if (countdown <= 0)
+ return target;
+
+ --countdown;
+ currentValue += step;
+ return currentValue;
+ }
+
+ private:
+ float currentValue, target, step;
+ int countdown, stepsToTarget;
+
+ JUCE_DECLARE_NON_COPYABLE (LinearSmoothedValue)
+ };
+
+ //==============================================================================
+ enum { numCombs = 8, numAllPasses = 4, numChannels = 2 };
+
+ Parameters parameters;
+ float gain;
+
+ CombFilter comb [numChannels][numCombs];
+ AllPassFilter allPass [numChannels][numAllPasses];
+
+ LinearSmoothedValue damping, feedback, dryGain, wetGain1, wetGain2;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Reverb)
+};
+
+
+#endif // JUCE_REVERB_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.cpp b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.cpp
new file mode 100644
index 0000000..da98659
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.cpp
@@ -0,0 +1,94 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#if defined (JUCE_AUDIO_BASICS_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE
+ /* When you add this cpp file to your project, you mustn't include it in a file where you've
+ already included any other headers - just put it inside a file on its own, possibly with your config
+ flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
+ header files that the compiler may be using.
+ */
+ #error "Incorrect use of JUCE cpp file"
+#endif
+
+// Your project must contain an AppConfig.h file with your project-specific settings in it,
+// and your header search path must make it accessible to the module's files.
+#include "AppConfig.h"
+#include "juce_audio_basics.h"
+
+#if JUCE_MINGW && ! defined (__SSE2__)
+ #define JUCE_USE_SSE_INTRINSICS 0
+#endif
+
+#ifndef JUCE_USE_SSE_INTRINSICS
+ #define JUCE_USE_SSE_INTRINSICS 1
+#endif
+
+#if ! JUCE_INTEL
+ #undef JUCE_USE_SSE_INTRINSICS
+#endif
+
+#if JUCE_USE_SSE_INTRINSICS
+ #include
+#endif
+
+#ifndef JUCE_USE_VDSP_FRAMEWORK
+ #define JUCE_USE_VDSP_FRAMEWORK 1
+#endif
+
+#if (JUCE_MAC || JUCE_IOS) && JUCE_USE_VDSP_FRAMEWORK
+ #define Point CarbonDummyPointName // (workaround to avoid definition of "Point" by old Carbon headers)
+ #include
+ #undef Point
+#else
+ #undef JUCE_USE_VDSP_FRAMEWORK
+#endif
+
+#if __ARM_NEON__ && ! (JUCE_USE_VDSP_FRAMEWORK || defined (JUCE_USE_ARM_NEON))
+ #define JUCE_USE_ARM_NEON 1
+ #include
+#endif
+
+namespace juce
+{
+
+#include "buffers/juce_AudioDataConverters.cpp"
+#include "buffers/juce_AudioSampleBuffer.cpp"
+#include "buffers/juce_FloatVectorOperations.cpp"
+#include "effects/juce_IIRFilter.cpp"
+#include "effects/juce_LagrangeInterpolator.cpp"
+#include "midi/juce_MidiBuffer.cpp"
+#include "midi/juce_MidiFile.cpp"
+#include "midi/juce_MidiKeyboardState.cpp"
+#include "midi/juce_MidiMessage.cpp"
+#include "midi/juce_MidiMessageSequence.cpp"
+#include "sources/juce_BufferingAudioSource.cpp"
+#include "sources/juce_ChannelRemappingAudioSource.cpp"
+#include "sources/juce_IIRFilterAudioSource.cpp"
+#include "sources/juce_MixerAudioSource.cpp"
+#include "sources/juce_ResamplingAudioSource.cpp"
+#include "sources/juce_ReverbAudioSource.cpp"
+#include "sources/juce_ToneGeneratorAudioSource.cpp"
+#include "synthesisers/juce_Synthesiser.cpp"
+
+}
diff --git a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h
new file mode 100644
index 0000000..70c5432
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h
@@ -0,0 +1,59 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_AUDIO_BASICS_H_INCLUDED
+#define JUCE_AUDIO_BASICS_H_INCLUDED
+
+#include "../juce_core/juce_core.h"
+
+//=============================================================================
+namespace juce
+{
+
+#include "buffers/juce_AudioDataConverters.h"
+#include "buffers/juce_AudioSampleBuffer.h"
+#include "buffers/juce_FloatVectorOperations.h"
+#include "effects/juce_Decibels.h"
+#include "effects/juce_IIRFilter.h"
+#include "effects/juce_LagrangeInterpolator.h"
+#include "effects/juce_Reverb.h"
+#include "midi/juce_MidiMessage.h"
+#include "midi/juce_MidiBuffer.h"
+#include "midi/juce_MidiMessageSequence.h"
+#include "midi/juce_MidiFile.h"
+#include "midi/juce_MidiKeyboardState.h"
+#include "sources/juce_AudioSource.h"
+#include "sources/juce_PositionableAudioSource.h"
+#include "sources/juce_BufferingAudioSource.h"
+#include "sources/juce_ChannelRemappingAudioSource.h"
+#include "sources/juce_IIRFilterAudioSource.h"
+#include "sources/juce_MixerAudioSource.h"
+#include "sources/juce_ResamplingAudioSource.h"
+#include "sources/juce_ReverbAudioSource.h"
+#include "sources/juce_ToneGeneratorAudioSource.h"
+#include "synthesisers/juce_Synthesiser.h"
+
+}
+
+#endif // JUCE_AUDIO_BASICS_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.mm b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.mm
new file mode 100644
index 0000000..54083fd
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.mm
@@ -0,0 +1,25 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#include "juce_audio_basics.cpp"
diff --git a/JuceLibraryCode/modules/juce_audio_basics/juce_module_info b/JuceLibraryCode/modules/juce_audio_basics/juce_module_info
new file mode 100644
index 0000000..01874e6
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/juce_module_info
@@ -0,0 +1,24 @@
+{
+ "id": "juce_audio_basics",
+ "name": "JUCE audio and midi data classes",
+ "version": "3.1.1",
+ "description": "Classes for audio buffer manipulation, midi message handling, synthesis, etc",
+ "website": "http://www.juce.com/juce",
+ "license": "GPL/Commercial",
+
+ "dependencies": [ { "id": "juce_core", "version": "matching" } ],
+
+ "include": "juce_audio_basics.h",
+
+ "compile": [ { "file": "juce_audio_basics.cpp", "target": "! xcode" },
+ { "file": "juce_audio_basics.mm", "target": "xcode" } ],
+
+ "browse": [ "buffers/*",
+ "midi/*",
+ "effects/*",
+ "sources/*",
+ "synthesisers/*" ],
+
+ "OSXFrameworks": "Accelerate",
+ "iOSFrameworks": "Accelerate"
+}
diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp
new file mode 100644
index 0000000..43ab4c1
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp
@@ -0,0 +1,229 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+namespace MidiBufferHelpers
+{
+ inline int getEventTime (const void* const d) noexcept
+ {
+ return *static_cast (d);
+ }
+
+ inline uint16 getEventDataSize (const void* const d) noexcept
+ {
+ return *reinterpret_cast (static_cast (d) + sizeof (int32));
+ }
+
+ inline uint16 getEventTotalSize (const void* const d) noexcept
+ {
+ return getEventDataSize (d) + sizeof (int32) + sizeof (uint16);
+ }
+
+ static int findActualEventLength (const uint8* const data, const int maxBytes) noexcept
+ {
+ unsigned int byte = (unsigned int) *data;
+ int size = 0;
+
+ if (byte == 0xf0 || byte == 0xf7)
+ {
+ const uint8* d = data + 1;
+
+ while (d < data + maxBytes)
+ if (*d++ == 0xf7)
+ break;
+
+ size = (int) (d - data);
+ }
+ else if (byte == 0xff)
+ {
+ int n;
+ const int bytesLeft = MidiMessage::readVariableLengthVal (data + 1, n);
+ size = jmin (maxBytes, n + 2 + bytesLeft);
+ }
+ else if (byte >= 0x80)
+ {
+ size = jmin (maxBytes, MidiMessage::getMessageLengthFromFirstByte ((uint8) byte));
+ }
+
+ return size;
+ }
+
+ static uint8* findEventAfter (uint8* d, uint8* endData, const int samplePosition) noexcept
+ {
+ while (d < endData && getEventTime (d) <= samplePosition)
+ d += getEventTotalSize (d);
+
+ return d;
+ }
+}
+
+//==============================================================================
+MidiBuffer::MidiBuffer() noexcept {}
+MidiBuffer::~MidiBuffer() {}
+
+MidiBuffer::MidiBuffer (const MidiBuffer& other) noexcept : data (other.data) {}
+
+MidiBuffer& MidiBuffer::operator= (const MidiBuffer& other) noexcept
+{
+ data = other.data;
+ return *this;
+}
+
+MidiBuffer::MidiBuffer (const MidiMessage& message) noexcept
+{
+ addEvent (message, 0);
+}
+
+void MidiBuffer::swapWith (MidiBuffer& other) noexcept { data.swapWith (other.data); }
+void MidiBuffer::clear() noexcept { data.clearQuick(); }
+void MidiBuffer::ensureSize (size_t minimumNumBytes) { data.ensureStorageAllocated ((int) minimumNumBytes); }
+bool MidiBuffer::isEmpty() const noexcept { return data.size() == 0; }
+
+void MidiBuffer::clear (const int startSample, const int numSamples)
+{
+ uint8* const start = MidiBufferHelpers::findEventAfter (data.begin(), data.end(), startSample - 1);
+ uint8* const end = MidiBufferHelpers::findEventAfter (start, data.end(), startSample + numSamples - 1);
+
+ data.removeRange ((int) (start - data.begin()), (int) (end - data.begin()));
+}
+
+void MidiBuffer::addEvent (const MidiMessage& m, const int sampleNumber)
+{
+ addEvent (m.getRawData(), m.getRawDataSize(), sampleNumber);
+}
+
+void MidiBuffer::addEvent (const void* const newData, const int maxBytes, const int sampleNumber)
+{
+ const int numBytes = MidiBufferHelpers::findActualEventLength (static_cast (newData), maxBytes);
+
+ if (numBytes > 0)
+ {
+ const size_t newItemSize = (size_t) numBytes + sizeof (int32) + sizeof (uint16);
+ const int offset = (int) (MidiBufferHelpers::findEventAfter (data.begin(), data.end(), sampleNumber) - data.begin());
+
+ data.insertMultiple (offset, 0, (int) newItemSize);
+
+ uint8* const d = data.begin() + offset;
+ *reinterpret_cast (d) = sampleNumber;
+ *reinterpret_cast (d + 4) = (uint16) numBytes;
+ memcpy (d + 6, newData, (size_t) numBytes);
+ }
+}
+
+void MidiBuffer::addEvents (const MidiBuffer& otherBuffer,
+ const int startSample,
+ const int numSamples,
+ const int sampleDeltaToAdd)
+{
+ Iterator i (otherBuffer);
+ i.setNextSamplePosition (startSample);
+
+ const uint8* eventData;
+ int eventSize, position;
+
+ while (i.getNextEvent (eventData, eventSize, position)
+ && (position < startSample + numSamples || numSamples < 0))
+ {
+ addEvent (eventData, eventSize, position + sampleDeltaToAdd);
+ }
+}
+
+int MidiBuffer::getNumEvents() const noexcept
+{
+ int n = 0;
+ const uint8* const end = data.end();
+
+ for (const uint8* d = data.begin(); d < end; ++n)
+ d += MidiBufferHelpers::getEventTotalSize (d);
+
+ return n;
+}
+
+int MidiBuffer::getFirstEventTime() const noexcept
+{
+ return data.size() > 0 ? MidiBufferHelpers::getEventTime (data.begin()) : 0;
+}
+
+int MidiBuffer::getLastEventTime() const noexcept
+{
+ if (data.size() == 0)
+ return 0;
+
+ const uint8* const endData = data.end();
+
+ for (const uint8* d = data.begin();;)
+ {
+ const uint8* const nextOne = d + MidiBufferHelpers::getEventTotalSize (d);
+
+ if (nextOne >= endData)
+ return MidiBufferHelpers::getEventTime (d);
+
+ d = nextOne;
+ }
+}
+
+//==============================================================================
+MidiBuffer::Iterator::Iterator (const MidiBuffer& b) noexcept
+ : buffer (b), data (b.data.begin())
+{
+}
+
+MidiBuffer::Iterator::~Iterator() noexcept
+{
+}
+
+void MidiBuffer::Iterator::setNextSamplePosition (const int samplePosition) noexcept
+{
+ data = buffer.data.begin();
+ const uint8* const dataEnd = buffer.data.end();
+
+ while (data < dataEnd && MidiBufferHelpers::getEventTime (data) < samplePosition)
+ data += MidiBufferHelpers::getEventTotalSize (data);
+}
+
+bool MidiBuffer::Iterator::getNextEvent (const uint8* &midiData, int& numBytes, int& samplePosition) noexcept
+{
+ if (data >= buffer.data.end())
+ return false;
+
+ samplePosition = MidiBufferHelpers::getEventTime (data);
+ const int itemSize = MidiBufferHelpers::getEventDataSize (data);
+ numBytes = itemSize;
+ midiData = data + sizeof (int32) + sizeof (uint16);
+ data += sizeof (int32) + sizeof (uint16) + (size_t) itemSize;
+
+ return true;
+}
+
+bool MidiBuffer::Iterator::getNextEvent (MidiMessage& result, int& samplePosition) noexcept
+{
+ if (data >= buffer.data.end())
+ return false;
+
+ samplePosition = MidiBufferHelpers::getEventTime (data);
+ const int itemSize = MidiBufferHelpers::getEventDataSize (data);
+ result = MidiMessage (data + sizeof (int32) + sizeof (uint16), itemSize, samplePosition);
+ data += sizeof (int32) + sizeof (uint16) + (size_t) itemSize;
+
+ return true;
+}
diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.h b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.h
new file mode 100644
index 0000000..ecbc56a
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.h
@@ -0,0 +1,235 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_MIDIBUFFER_H_INCLUDED
+#define JUCE_MIDIBUFFER_H_INCLUDED
+
+
+//==============================================================================
+/**
+ Holds a sequence of time-stamped midi events.
+
+ Analogous to the AudioSampleBuffer, this holds a set of midi events with
+ integer time-stamps. The buffer is kept sorted in order of the time-stamps.
+
+ If you're working with a sequence of midi events that may need to be manipulated
+ or read/written to a midi file, then MidiMessageSequence is probably a more
+ appropriate container. MidiBuffer is designed for lower-level streams of raw
+ midi data.
+
+ @see MidiMessage
+*/
+class JUCE_API MidiBuffer
+{
+public:
+ //==============================================================================
+ /** Creates an empty MidiBuffer. */
+ MidiBuffer() noexcept;
+
+ /** Creates a MidiBuffer containing a single midi message. */
+ explicit MidiBuffer (const MidiMessage& message) noexcept;
+
+ /** Creates a copy of another MidiBuffer. */
+ MidiBuffer (const MidiBuffer&) noexcept;
+
+ /** Makes a copy of another MidiBuffer. */
+ MidiBuffer& operator= (const MidiBuffer&) noexcept;
+
+ /** Destructor */
+ ~MidiBuffer();
+
+ //==============================================================================
+ /** Removes all events from the buffer. */
+ void clear() noexcept;
+
+ /** Removes all events between two times from the buffer.
+
+ All events for which (start <= event position < start + numSamples) will
+ be removed.
+ */
+ void clear (int start, int numSamples);
+
+ /** Returns true if the buffer is empty.
+ To actually retrieve the events, use a MidiBuffer::Iterator object
+ */
+ bool isEmpty() const noexcept;
+
+ /** Counts the number of events in the buffer.
+
+ This is actually quite a slow operation, as it has to iterate through all
+ the events, so you might prefer to call isEmpty() if that's all you need
+ to know.
+ */
+ int getNumEvents() const noexcept;
+
+ /** Adds an event to the buffer.
+
+ The sample number will be used to determine the position of the event in
+ the buffer, which is always kept sorted. The MidiMessage's timestamp is
+ ignored.
+
+ If an event is added whose sample position is the same as one or more events
+ already in the buffer, the new event will be placed after the existing ones.
+
+ To retrieve events, use a MidiBuffer::Iterator object
+ */
+ void addEvent (const MidiMessage& midiMessage, int sampleNumber);
+
+ /** Adds an event to the buffer from raw midi data.
+
+ The sample number will be used to determine the position of the event in
+ the buffer, which is always kept sorted.
+
+ If an event is added whose sample position is the same as one or more events
+ already in the buffer, the new event will be placed after the existing ones.
+
+ The event data will be inspected to calculate the number of bytes in length that
+ the midi event really takes up, so maxBytesOfMidiData may be longer than the data
+ that actually gets stored. E.g. if you pass in a note-on and a length of 4 bytes,
+ it'll actually only store 3 bytes. If the midi data is invalid, it might not
+ add an event at all.
+
+ To retrieve events, use a MidiBuffer::Iterator object
+ */
+ void addEvent (const void* rawMidiData,
+ int maxBytesOfMidiData,
+ int sampleNumber);
+
+ /** Adds some events from another buffer to this one.
+
+ @param otherBuffer the buffer containing the events you want to add
+ @param startSample the lowest sample number in the source buffer for which
+ events should be added. Any source events whose timestamp is
+ less than this will be ignored
+ @param numSamples the valid range of samples from the source buffer for which
+ events should be added - i.e. events in the source buffer whose
+ timestamp is greater than or equal to (startSample + numSamples)
+ will be ignored. If this value is less than 0, all events after
+ startSample will be taken.
+ @param sampleDeltaToAdd a value which will be added to the source timestamps of the events
+ that are added to this buffer
+ */
+ void addEvents (const MidiBuffer& otherBuffer,
+ int startSample,
+ int numSamples,
+ int sampleDeltaToAdd);
+
+ /** Returns the sample number of the first event in the buffer.
+ If the buffer's empty, this will just return 0.
+ */
+ int getFirstEventTime() const noexcept;
+
+ /** Returns the sample number of the last event in the buffer.
+ If the buffer's empty, this will just return 0.
+ */
+ int getLastEventTime() const noexcept;
+
+ //==============================================================================
+ /** Exchanges the contents of this buffer with another one.
+
+ This is a quick operation, because no memory allocating or copying is done, it
+ just swaps the internal state of the two buffers.
+ */
+ void swapWith (MidiBuffer&) noexcept;
+
+ /** Preallocates some memory for the buffer to use.
+ This helps to avoid needing to reallocate space when the buffer has messages
+ added to it.
+ */
+ void ensureSize (size_t minimumNumBytes);
+
+ //==============================================================================
+ /**
+ Used to iterate through the events in a MidiBuffer.
+
+ Note that altering the buffer while an iterator is using it isn't a
+ safe operation.
+
+ @see MidiBuffer
+ */
+ class JUCE_API Iterator
+ {
+ public:
+ //==============================================================================
+ /** Creates an Iterator for this MidiBuffer. */
+ Iterator (const MidiBuffer&) noexcept;
+
+ /** Destructor. */
+ ~Iterator() noexcept;
+
+ //==============================================================================
+ /** Repositions the iterator so that the next event retrieved will be the first
+ one whose sample position is at greater than or equal to the given position.
+ */
+ void setNextSamplePosition (int samplePosition) noexcept;
+
+ /** Retrieves a copy of the next event from the buffer.
+
+ @param result on return, this will be the message. The MidiMessage's timestamp
+ is set to the same value as samplePosition.
+ @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
+ the end of the buffer
+ */
+ bool getNextEvent (MidiMessage& result,
+ int& samplePosition) noexcept;
+
+ /** Retrieves the next event from the buffer.
+
+ @param midiData on return, this pointer will be set to a block of data containing
+ the midi message. Note that to make it fast, this is a pointer
+ directly into the MidiBuffer's internal data, so is only valid
+ temporarily until the MidiBuffer is altered.
+ @param numBytesOfMidiData on return, this is the number of bytes of data used by the
+ midi message
+ @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
+ the end of the buffer
+ */
+ bool getNextEvent (const uint8* &midiData,
+ int& numBytesOfMidiData,
+ int& samplePosition) noexcept;
+
+ private:
+ //==============================================================================
+ const MidiBuffer& buffer;
+ const uint8* data;
+
+ JUCE_DECLARE_NON_COPYABLE (Iterator)
+ };
+
+ /** The raw data holding this buffer.
+ Obviously access to this data is provided at your own risk. Its internal format could
+ change in future, so don't write code that relies on it!
+ */
+ Array data;
+
+private:
+ JUCE_LEAK_DETECTOR (MidiBuffer)
+};
+
+
+#endif // JUCE_MIDIBUFFER_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp
new file mode 100644
index 0000000..e798cc0
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp
@@ -0,0 +1,428 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+namespace MidiFileHelpers
+{
+ static void writeVariableLengthInt (OutputStream& out, unsigned int v)
+ {
+ unsigned int buffer = v & 0x7f;
+
+ while ((v >>= 7) != 0)
+ {
+ buffer <<= 8;
+ buffer |= ((v & 0x7f) | 0x80);
+ }
+
+ for (;;)
+ {
+ out.writeByte ((char) buffer);
+
+ if (buffer & 0x80)
+ buffer >>= 8;
+ else
+ break;
+ }
+ }
+
+ static bool parseMidiHeader (const uint8* &data, short& timeFormat, short& fileType, short& numberOfTracks) noexcept
+ {
+ unsigned int ch = ByteOrder::bigEndianInt (data);
+ data += 4;
+
+ if (ch != ByteOrder::bigEndianInt ("MThd"))
+ {
+ bool ok = false;
+
+ if (ch == ByteOrder::bigEndianInt ("RIFF"))
+ {
+ for (int i = 0; i < 8; ++i)
+ {
+ ch = ByteOrder::bigEndianInt (data);
+ data += 4;
+
+ if (ch == ByteOrder::bigEndianInt ("MThd"))
+ {
+ ok = true;
+ break;
+ }
+ }
+ }
+
+ if (! ok)
+ return false;
+ }
+
+ unsigned int bytesRemaining = ByteOrder::bigEndianInt (data);
+ data += 4;
+ fileType = (short) ByteOrder::bigEndianShort (data);
+ data += 2;
+ numberOfTracks = (short) ByteOrder::bigEndianShort (data);
+ data += 2;
+ timeFormat = (short) ByteOrder::bigEndianShort (data);
+ data += 2;
+ bytesRemaining -= 6;
+ data += bytesRemaining;
+
+ return true;
+ }
+
+ static double convertTicksToSeconds (const double time,
+ const MidiMessageSequence& tempoEvents,
+ const int timeFormat)
+ {
+ if (timeFormat < 0)
+ return time / (-(timeFormat >> 8) * (timeFormat & 0xff));
+
+ double lastTime = 0.0, correctedTime = 0.0;
+ const double tickLen = 1.0 / (timeFormat & 0x7fff);
+ double secsPerTick = 0.5 * tickLen;
+ const int numEvents = tempoEvents.getNumEvents();
+
+ for (int i = 0; i < numEvents; ++i)
+ {
+ const MidiMessage& m = tempoEvents.getEventPointer(i)->message;
+ const double eventTime = m.getTimeStamp();
+
+ if (eventTime >= time)
+ break;
+
+ correctedTime += (eventTime - lastTime) * secsPerTick;
+ lastTime = eventTime;
+
+ if (m.isTempoMetaEvent())
+ secsPerTick = tickLen * m.getTempoSecondsPerQuarterNote();
+
+ while (i + 1 < numEvents)
+ {
+ const MidiMessage& m2 = tempoEvents.getEventPointer(i + 1)->message;
+
+ if (m2.getTimeStamp() != eventTime)
+ break;
+
+ if (m2.isTempoMetaEvent())
+ secsPerTick = tickLen * m2.getTempoSecondsPerQuarterNote();
+
+ ++i;
+ }
+ }
+
+ return correctedTime + (time - lastTime) * secsPerTick;
+ }
+
+ // a comparator that puts all the note-offs before note-ons that have the same time
+ struct Sorter
+ {
+ static int compareElements (const MidiMessageSequence::MidiEventHolder* const first,
+ const MidiMessageSequence::MidiEventHolder* const second) noexcept
+ {
+ const double diff = (first->message.getTimeStamp() - second->message.getTimeStamp());
+
+ if (diff > 0) return 1;
+ if (diff < 0) return -1;
+ if (first->message.isNoteOff() && second->message.isNoteOn()) return -1;
+ if (first->message.isNoteOn() && second->message.isNoteOff()) return 1;
+
+ return 0;
+ }
+ };
+
+ template
+ static void findAllMatchingEvents (const OwnedArray& tracks,
+ MidiMessageSequence& results,
+ MethodType method)
+ {
+ for (int i = 0; i < tracks.size(); ++i)
+ {
+ const MidiMessageSequence& track = *tracks.getUnchecked(i);
+ const int numEvents = track.getNumEvents();
+
+ for (int j = 0; j < numEvents; ++j)
+ {
+ const MidiMessage& m = track.getEventPointer(j)->message;
+
+ if ((m.*method)())
+ results.addEvent (m);
+ }
+ }
+ }
+}
+
+//==============================================================================
+MidiFile::MidiFile()
+ : timeFormat ((short) (unsigned short) 0xe728)
+{
+}
+
+MidiFile::~MidiFile()
+{
+}
+
+void MidiFile::clear()
+{
+ tracks.clear();
+}
+
+//==============================================================================
+int MidiFile::getNumTracks() const noexcept
+{
+ return tracks.size();
+}
+
+const MidiMessageSequence* MidiFile::getTrack (const int index) const noexcept
+{
+ return tracks [index];
+}
+
+void MidiFile::addTrack (const MidiMessageSequence& trackSequence)
+{
+ tracks.add (new MidiMessageSequence (trackSequence));
+}
+
+//==============================================================================
+short MidiFile::getTimeFormat() const noexcept
+{
+ return timeFormat;
+}
+
+void MidiFile::setTicksPerQuarterNote (const int ticks) noexcept
+{
+ timeFormat = (short) ticks;
+}
+
+void MidiFile::setSmpteTimeFormat (const int framesPerSecond,
+ const int subframeResolution) noexcept
+{
+ timeFormat = (short) (((-framesPerSecond) << 8) | subframeResolution);
+}
+
+//==============================================================================
+void MidiFile::findAllTempoEvents (MidiMessageSequence& results) const
+{
+ MidiFileHelpers::findAllMatchingEvents (tracks, results, &MidiMessage::isTempoMetaEvent);
+}
+
+void MidiFile::findAllTimeSigEvents (MidiMessageSequence& results) const
+{
+ MidiFileHelpers::findAllMatchingEvents (tracks, results, &MidiMessage::isTimeSignatureMetaEvent);
+}
+
+void MidiFile::findAllKeySigEvents (MidiMessageSequence& results) const
+{
+ MidiFileHelpers::findAllMatchingEvents (tracks, results, &MidiMessage::isKeySignatureMetaEvent);
+}
+
+double MidiFile::getLastTimestamp() const
+{
+ double t = 0.0;
+
+ for (int i = tracks.size(); --i >= 0;)
+ t = jmax (t, tracks.getUnchecked(i)->getEndTime());
+
+ return t;
+}
+
+//==============================================================================
+bool MidiFile::readFrom (InputStream& sourceStream)
+{
+ clear();
+ MemoryBlock data;
+
+ const int maxSensibleMidiFileSize = 2 * 1024 * 1024;
+
+ // (put a sanity-check on the file size, as midi files are generally small)
+ if (sourceStream.readIntoMemoryBlock (data, maxSensibleMidiFileSize))
+ {
+ size_t size = data.getSize();
+ const uint8* d = static_cast (data.getData());
+ short fileType, expectedTracks;
+
+ if (size > 16 && MidiFileHelpers::parseMidiHeader (d, timeFormat, fileType, expectedTracks))
+ {
+ size -= (size_t) (d - static_cast (data.getData()));
+
+ int track = 0;
+
+ while (size > 0 && track < expectedTracks)
+ {
+ const int chunkType = (int) ByteOrder::bigEndianInt (d);
+ d += 4;
+ const int chunkSize = (int) ByteOrder::bigEndianInt (d);
+ d += 4;
+
+ if (chunkSize <= 0)
+ break;
+
+ if (chunkType == (int) ByteOrder::bigEndianInt ("MTrk"))
+ readNextTrack (d, chunkSize);
+
+ size -= (size_t) chunkSize + 8;
+ d += chunkSize;
+ ++track;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void MidiFile::readNextTrack (const uint8* data, int size)
+{
+ double time = 0;
+ uint8 lastStatusByte = 0;
+
+ MidiMessageSequence result;
+
+ while (size > 0)
+ {
+ int bytesUsed;
+ const int delay = MidiMessage::readVariableLengthVal (data, bytesUsed);
+ data += bytesUsed;
+ size -= bytesUsed;
+ time += delay;
+
+ int messSize = 0;
+ const MidiMessage mm (data, size, messSize, lastStatusByte, time);
+
+ if (messSize <= 0)
+ break;
+
+ size -= messSize;
+ data += messSize;
+
+ result.addEvent (mm);
+
+ const uint8 firstByte = *(mm.getRawData());
+ if ((firstByte & 0xf0) != 0xf0)
+ lastStatusByte = firstByte;
+ }
+
+ // use a sort that puts all the note-offs before note-ons that have the same time
+ MidiFileHelpers::Sorter sorter;
+ result.list.sort (sorter, true);
+
+ addTrack (result);
+ tracks.getLast()->updateMatchedPairs();
+}
+
+//==============================================================================
+void MidiFile::convertTimestampTicksToSeconds()
+{
+ MidiMessageSequence tempoEvents;
+ findAllTempoEvents (tempoEvents);
+ findAllTimeSigEvents (tempoEvents);
+
+ if (timeFormat != 0)
+ {
+ for (int i = 0; i < tracks.size(); ++i)
+ {
+ const MidiMessageSequence& ms = *tracks.getUnchecked(i);
+
+ for (int j = ms.getNumEvents(); --j >= 0;)
+ {
+ MidiMessage& m = ms.getEventPointer(j)->message;
+ m.setTimeStamp (MidiFileHelpers::convertTicksToSeconds (m.getTimeStamp(), tempoEvents, timeFormat));
+ }
+ }
+ }
+}
+
+//==============================================================================
+bool MidiFile::writeTo (OutputStream& out, int midiFileType)
+{
+ jassert (midiFileType >= 0 && midiFileType <= 2);
+
+ out.writeIntBigEndian ((int) ByteOrder::bigEndianInt ("MThd"));
+ out.writeIntBigEndian (6);
+ out.writeShortBigEndian ((short) midiFileType);
+ out.writeShortBigEndian ((short) tracks.size());
+ out.writeShortBigEndian (timeFormat);
+
+ for (int i = 0; i < tracks.size(); ++i)
+ writeTrack (out, i);
+
+ out.flush();
+ return true;
+}
+
+void MidiFile::writeTrack (OutputStream& mainOut, const int trackNum)
+{
+ MemoryOutputStream out;
+ const MidiMessageSequence& ms = *tracks.getUnchecked (trackNum);
+
+ int lastTick = 0;
+ uint8 lastStatusByte = 0;
+ bool endOfTrackEventWritten = false;
+
+ for (int i = 0; i < ms.getNumEvents(); ++i)
+ {
+ const MidiMessage& mm = ms.getEventPointer(i)->message;
+
+ if (mm.isEndOfTrackMetaEvent())
+ endOfTrackEventWritten = true;
+
+ const int tick = roundToInt (mm.getTimeStamp());
+ const int delta = jmax (0, tick - lastTick);
+ MidiFileHelpers::writeVariableLengthInt (out, (uint32) delta);
+ lastTick = tick;
+
+ const uint8* data = mm.getRawData();
+ int dataSize = mm.getRawDataSize();
+
+ const uint8 statusByte = data[0];
+
+ if (statusByte == lastStatusByte
+ && (statusByte & 0xf0) != 0xf0
+ && dataSize > 1
+ && i > 0)
+ {
+ ++data;
+ --dataSize;
+ }
+ else if (statusByte == 0xf0) // Write sysex message with length bytes.
+ {
+ out.writeByte ((char) statusByte);
+
+ ++data;
+ --dataSize;
+
+ MidiFileHelpers::writeVariableLengthInt (out, (uint32) dataSize);
+ }
+
+ out.write (data, (size_t) dataSize);
+ lastStatusByte = statusByte;
+ }
+
+ if (! endOfTrackEventWritten)
+ {
+ out.writeByte (0); // (tick delta)
+ const MidiMessage m (MidiMessage::endOfTrack());
+ out.write (m.getRawData(), (size_t) m.getRawDataSize());
+ }
+
+ mainOut.writeIntBigEndian ((int) ByteOrder::bigEndianInt ("MTrk"));
+ mainOut.writeIntBigEndian ((int) out.getDataSize());
+ mainOut << out;
+}
diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.h b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.h
new file mode 100644
index 0000000..2a69937
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.h
@@ -0,0 +1,180 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_MIDIFILE_H_INCLUDED
+#define JUCE_MIDIFILE_H_INCLUDED
+
+
+//==============================================================================
+/**
+ Reads/writes standard midi format files.
+
+ To read a midi file, create a MidiFile object and call its readFrom() method. You
+ can then get the individual midi tracks from it using the getTrack() method.
+
+ To write a file, create a MidiFile object, add some MidiMessageSequence objects
+ to it using the addTrack() method, and then call its writeTo() method to stream
+ it out.
+
+ @see MidiMessageSequence
+*/
+class JUCE_API MidiFile
+{
+public:
+ //==============================================================================
+ /** Creates an empty MidiFile object.
+ */
+ MidiFile();
+
+ /** Destructor. */
+ ~MidiFile();
+
+ //==============================================================================
+ /** Returns the number of tracks in the file.
+ @see getTrack, addTrack
+ */
+ int getNumTracks() const noexcept;
+
+ /** Returns a pointer to one of the tracks in the file.
+ @returns a pointer to the track, or nullptr if the index is out-of-range
+ @see getNumTracks, addTrack
+ */
+ const MidiMessageSequence* getTrack (int index) const noexcept;
+
+ /** Adds a midi track to the file.
+ This will make its own internal copy of the sequence that is passed-in.
+ @see getNumTracks, getTrack
+ */
+ void addTrack (const MidiMessageSequence& trackSequence);
+
+ /** Removes all midi tracks from the file.
+ @see getNumTracks
+ */
+ void clear();
+
+ /** Returns the raw time format code that will be written to a stream.
+
+ After reading a midi file, this method will return the time-format that
+ was read from the file's header. It can be changed using the setTicksPerQuarterNote()
+ or setSmpteTimeFormat() methods.
+
+ If the value returned is positive, it indicates the number of midi ticks
+ per quarter-note - see setTicksPerQuarterNote().
+
+ It it's negative, the upper byte indicates the frames-per-second (but negative), and
+ the lower byte is the number of ticks per frame - see setSmpteTimeFormat().
+ */
+ short getTimeFormat() const noexcept;
+
+ /** Sets the time format to use when this file is written to a stream.
+
+ If this is called, the file will be written as bars/beats using the
+ specified resolution, rather than SMPTE absolute times, as would be
+ used if setSmpteTimeFormat() had been called instead.
+
+ @param ticksPerQuarterNote e.g. 96, 960
+ @see setSmpteTimeFormat
+ */
+ void setTicksPerQuarterNote (int ticksPerQuarterNote) noexcept;
+
+ /** Sets the time format to use when this file is written to a stream.
+
+ If this is called, the file will be written using absolute times, rather
+ than bars/beats as would be the case if setTicksPerBeat() had been called
+ instead.
+
+ @param framesPerSecond must be 24, 25, 29 or 30
+ @param subframeResolution the sub-second resolution, e.g. 4 (midi time code),
+ 8, 10, 80 (SMPTE bit resolution), or 100. For millisecond
+ timing, setSmpteTimeFormat (25, 40)
+ @see setTicksPerBeat
+ */
+ void setSmpteTimeFormat (int framesPerSecond,
+ int subframeResolution) noexcept;
+
+ //==============================================================================
+ /** Makes a list of all the tempo-change meta-events from all tracks in the midi file.
+ Useful for finding the positions of all the tempo changes in a file.
+ @param tempoChangeEvents a list to which all the events will be added
+ */
+ void findAllTempoEvents (MidiMessageSequence& tempoChangeEvents) const;
+
+ /** Makes a list of all the time-signature meta-events from all tracks in the midi file.
+ Useful for finding the positions of all the tempo changes in a file.
+ @param timeSigEvents a list to which all the events will be added
+ */
+ void findAllTimeSigEvents (MidiMessageSequence& timeSigEvents) const;
+
+ /** Makes a list of all the time-signature meta-events from all tracks in the midi file.
+ @param keySigEvents a list to which all the events will be added
+ */
+ void findAllKeySigEvents (MidiMessageSequence& keySigEvents) const;
+
+ /** Returns the latest timestamp in any of the tracks.
+ (Useful for finding the length of the file).
+ */
+ double getLastTimestamp() const;
+
+ //==============================================================================
+ /** Reads a midi file format stream.
+
+ After calling this, you can get the tracks that were read from the file by using the
+ getNumTracks() and getTrack() methods.
+
+ The timestamps of the midi events in the tracks will represent their positions in
+ terms of midi ticks. To convert them to seconds, use the convertTimestampTicksToSeconds()
+ method.
+
+ @returns true if the stream was read successfully
+ */
+ bool readFrom (InputStream& sourceStream);
+
+ /** Writes the midi tracks as a standard midi file.
+ The midiFileType value is written as the file's format type, which can be 0, 1
+ or 2 - see the midi file spec for more info about that.
+ @returns true if the operation succeeded.
+ */
+ bool writeTo (OutputStream& destStream, int midiFileType = 1);
+
+ /** Converts the timestamp of all the midi events from midi ticks to seconds.
+
+ This will use the midi time format and tempo/time signature info in the
+ tracks to convert all the timestamps to absolute values in seconds.
+ */
+ void convertTimestampTicksToSeconds();
+
+
+private:
+ //==============================================================================
+ OwnedArray tracks;
+ short timeFormat;
+
+ void readNextTrack (const uint8*, int size);
+ void writeTrack (OutputStream&, int trackNum);
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiFile)
+};
+
+
+#endif // JUCE_MIDIFILE_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp
new file mode 100644
index 0000000..a40262b
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp
@@ -0,0 +1,183 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+MidiKeyboardState::MidiKeyboardState()
+{
+ zerostruct (noteStates);
+}
+
+MidiKeyboardState::~MidiKeyboardState()
+{
+}
+
+//==============================================================================
+void MidiKeyboardState::reset()
+{
+ const ScopedLock sl (lock);
+ zerostruct (noteStates);
+ eventsToAdd.clear();
+}
+
+bool MidiKeyboardState::isNoteOn (const int midiChannel, const int n) const noexcept
+{
+ jassert (midiChannel >= 0 && midiChannel <= 16);
+
+ return isPositiveAndBelow (n, (int) 128)
+ && (noteStates[n] & (1 << (midiChannel - 1))) != 0;
+}
+
+bool MidiKeyboardState::isNoteOnForChannels (const int midiChannelMask, const int n) const noexcept
+{
+ return isPositiveAndBelow (n, (int) 128)
+ && (noteStates[n] & midiChannelMask) != 0;
+}
+
+void MidiKeyboardState::noteOn (const int midiChannel, const int midiNoteNumber, const float velocity)
+{
+ jassert (midiChannel >= 0 && midiChannel <= 16);
+ jassert (isPositiveAndBelow (midiNoteNumber, (int) 128));
+
+ const ScopedLock sl (lock);
+
+ if (isPositiveAndBelow (midiNoteNumber, (int) 128))
+ {
+ const int timeNow = (int) Time::getMillisecondCounter();
+ eventsToAdd.addEvent (MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity), timeNow);
+ eventsToAdd.clear (0, timeNow - 500);
+
+ noteOnInternal (midiChannel, midiNoteNumber, velocity);
+ }
+}
+
+void MidiKeyboardState::noteOnInternal (const int midiChannel, const int midiNoteNumber, const float velocity)
+{
+ if (isPositiveAndBelow (midiNoteNumber, (int) 128))
+ {
+ noteStates [midiNoteNumber] |= (1 << (midiChannel - 1));
+
+ for (int i = listeners.size(); --i >= 0;)
+ listeners.getUnchecked(i)->handleNoteOn (this, midiChannel, midiNoteNumber, velocity);
+ }
+}
+
+void MidiKeyboardState::noteOff (const int midiChannel, const int midiNoteNumber)
+{
+ const ScopedLock sl (lock);
+
+ if (isNoteOn (midiChannel, midiNoteNumber))
+ {
+ const int timeNow = (int) Time::getMillisecondCounter();
+ eventsToAdd.addEvent (MidiMessage::noteOff (midiChannel, midiNoteNumber), timeNow);
+ eventsToAdd.clear (0, timeNow - 500);
+
+ noteOffInternal (midiChannel, midiNoteNumber);
+ }
+}
+
+void MidiKeyboardState::noteOffInternal (const int midiChannel, const int midiNoteNumber)
+{
+ if (isNoteOn (midiChannel, midiNoteNumber))
+ {
+ noteStates [midiNoteNumber] &= ~(1 << (midiChannel - 1));
+
+ for (int i = listeners.size(); --i >= 0;)
+ listeners.getUnchecked(i)->handleNoteOff (this, midiChannel, midiNoteNumber);
+ }
+}
+
+void MidiKeyboardState::allNotesOff (const int midiChannel)
+{
+ const ScopedLock sl (lock);
+
+ if (midiChannel <= 0)
+ {
+ for (int i = 1; i <= 16; ++i)
+ allNotesOff (i);
+ }
+ else
+ {
+ for (int i = 0; i < 128; ++i)
+ noteOff (midiChannel, i);
+ }
+}
+
+void MidiKeyboardState::processNextMidiEvent (const MidiMessage& message)
+{
+ if (message.isNoteOn())
+ {
+ noteOnInternal (message.getChannel(), message.getNoteNumber(), message.getFloatVelocity());
+ }
+ else if (message.isNoteOff())
+ {
+ noteOffInternal (message.getChannel(), message.getNoteNumber());
+ }
+ else if (message.isAllNotesOff())
+ {
+ for (int i = 0; i < 128; ++i)
+ noteOffInternal (message.getChannel(), i);
+ }
+}
+
+void MidiKeyboardState::processNextMidiBuffer (MidiBuffer& buffer,
+ const int startSample,
+ const int numSamples,
+ const bool injectIndirectEvents)
+{
+ MidiBuffer::Iterator i (buffer);
+ MidiMessage message (0xf4, 0.0);
+ int time;
+
+ const ScopedLock sl (lock);
+
+ while (i.getNextEvent (message, time))
+ processNextMidiEvent (message);
+
+ if (injectIndirectEvents)
+ {
+ MidiBuffer::Iterator i2 (eventsToAdd);
+ const int firstEventToAdd = eventsToAdd.getFirstEventTime();
+ const double scaleFactor = numSamples / (double) (eventsToAdd.getLastEventTime() + 1 - firstEventToAdd);
+
+ while (i2.getNextEvent (message, time))
+ {
+ const int pos = jlimit (0, numSamples - 1, roundToInt ((time - firstEventToAdd) * scaleFactor));
+ buffer.addEvent (message, startSample + pos);
+ }
+ }
+
+ eventsToAdd.clear();
+}
+
+//==============================================================================
+void MidiKeyboardState::addListener (MidiKeyboardStateListener* const listener)
+{
+ const ScopedLock sl (lock);
+ listeners.addIfNotAlreadyThere (listener);
+}
+
+void MidiKeyboardState::removeListener (MidiKeyboardStateListener* const listener)
+{
+ const ScopedLock sl (lock);
+ listeners.removeFirstMatchingValue (listener);
+}
diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h
new file mode 100644
index 0000000..885b7d8
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h
@@ -0,0 +1,205 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_MIDIKEYBOARDSTATE_H_INCLUDED
+#define JUCE_MIDIKEYBOARDSTATE_H_INCLUDED
+
+class MidiKeyboardState;
+
+
+//==============================================================================
+/**
+ Receives events from a MidiKeyboardState object.
+
+ @see MidiKeyboardState
+*/
+class JUCE_API MidiKeyboardStateListener
+{
+public:
+ //==============================================================================
+ MidiKeyboardStateListener() noexcept {}
+ virtual ~MidiKeyboardStateListener() {}
+
+ //==============================================================================
+ /** Called when one of the MidiKeyboardState's keys is pressed.
+
+ This will be called synchronously when the state is either processing a
+ buffer in its MidiKeyboardState::processNextMidiBuffer() method, or
+ when a note is being played with its MidiKeyboardState::noteOn() method.
+
+ Note that this callback could happen from an audio callback thread, so be
+ careful not to block, and avoid any UI activity in the callback.
+ */
+ virtual void handleNoteOn (MidiKeyboardState* source,
+ int midiChannel, int midiNoteNumber, float velocity) = 0;
+
+ /** Called when one of the MidiKeyboardState's keys is released.
+
+ This will be called synchronously when the state is either processing a
+ buffer in its MidiKeyboardState::processNextMidiBuffer() method, or
+ when a note is being played with its MidiKeyboardState::noteOff() method.
+
+ Note that this callback could happen from an audio callback thread, so be
+ careful not to block, and avoid any UI activity in the callback.
+ */
+ virtual void handleNoteOff (MidiKeyboardState* source,
+ int midiChannel, int midiNoteNumber) = 0;
+};
+
+
+//==============================================================================
+/**
+ Represents a piano keyboard, keeping track of which keys are currently pressed.
+
+ This object can parse a stream of midi events, using them to update its idea
+ of which keys are pressed for each individiual midi channel.
+
+ When keys go up or down, it can broadcast these events to listener objects.
+
+ It also allows key up/down events to be triggered with its noteOn() and noteOff()
+ methods, and midi messages for these events will be merged into the
+ midi stream that gets processed by processNextMidiBuffer().
+*/
+class JUCE_API MidiKeyboardState
+{
+public:
+ //==============================================================================
+ MidiKeyboardState();
+ ~MidiKeyboardState();
+
+ //==============================================================================
+ /** Resets the state of the object.
+
+ All internal data for all the channels is reset, but no events are sent as a
+ result.
+
+ If you want to release any keys that are currently down, and to send out note-up
+ midi messages for this, use the allNotesOff() method instead.
+ */
+ void reset();
+
+ /** Returns true if the given midi key is currently held down for the given midi channel.
+
+ The channel number must be between 1 and 16. If you want to see if any notes are
+ on for a range of channels, use the isNoteOnForChannels() method.
+ */
+ bool isNoteOn (int midiChannel, int midiNoteNumber) const noexcept;
+
+ /** Returns true if the given midi key is currently held down on any of a set of midi channels.
+
+ The channel mask has a bit set for each midi channel you want to test for - bit
+ 0 = midi channel 1, bit 1 = midi channel 2, etc.
+
+ If a note is on for at least one of the specified channels, this returns true.
+ */
+ bool isNoteOnForChannels (int midiChannelMask, int midiNoteNumber) const noexcept;
+
+ /** Turns a specified note on.
+
+ This will cause a suitable midi note-on event to be injected into the midi buffer during the
+ next call to processNextMidiBuffer().
+
+ It will also trigger a synchronous callback to the listeners to tell them that the key has
+ gone down.
+ */
+ void noteOn (int midiChannel, int midiNoteNumber, float velocity);
+
+ /** Turns a specified note off.
+
+ This will cause a suitable midi note-off event to be injected into the midi buffer during the
+ next call to processNextMidiBuffer().
+
+ It will also trigger a synchronous callback to the listeners to tell them that the key has
+ gone up.
+
+ But if the note isn't acutally down for the given channel, this method will in fact do nothing.
+ */
+ void noteOff (int midiChannel, int midiNoteNumber);
+
+ /** This will turn off any currently-down notes for the given midi channel.
+
+ If you pass 0 for the midi channel, it will in fact turn off all notes on all channels.
+
+ Calling this method will make calls to noteOff(), so can trigger synchronous callbacks
+ and events being added to the midi stream.
+ */
+ void allNotesOff (int midiChannel);
+
+ //==============================================================================
+ /** Looks at a key-up/down event and uses it to update the state of this object.
+
+ To process a buffer full of midi messages, use the processNextMidiBuffer() method
+ instead.
+ */
+ void processNextMidiEvent (const MidiMessage& message);
+
+ /** Scans a midi stream for up/down events and adds its own events to it.
+
+ This will look for any up/down events and use them to update the internal state,
+ synchronously making suitable callbacks to the listeners.
+
+ If injectIndirectEvents is true, then midi events to produce the recent noteOn()
+ and noteOff() calls will be added into the buffer.
+
+ Only the section of the buffer whose timestamps are between startSample and
+ (startSample + numSamples) will be affected, and any events added will be placed
+ between these times.
+
+ If you're going to use this method, you'll need to keep calling it regularly for
+ it to work satisfactorily.
+
+ To process a single midi event at a time, use the processNextMidiEvent() method
+ instead.
+ */
+ void processNextMidiBuffer (MidiBuffer& buffer,
+ int startSample,
+ int numSamples,
+ bool injectIndirectEvents);
+
+ //==============================================================================
+ /** Registers a listener for callbacks when keys go up or down.
+ @see removeListener
+ */
+ void addListener (MidiKeyboardStateListener* listener);
+
+ /** Deregisters a listener.
+ @see addListener
+ */
+ void removeListener (MidiKeyboardStateListener* listener);
+
+private:
+ //==============================================================================
+ CriticalSection lock;
+ uint16 noteStates [128];
+ MidiBuffer eventsToAdd;
+ Array listeners;
+
+ void noteOnInternal (int midiChannel, int midiNoteNumber, float velocity);
+ void noteOffInternal (int midiChannel, int midiNoteNumber);
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiKeyboardState)
+};
+
+
+#endif // JUCE_MIDIKEYBOARDSTATE_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.cpp
new file mode 100644
index 0000000..07e2ccd
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.cpp
@@ -0,0 +1,1094 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+namespace MidiHelpers
+{
+ inline uint8 initialByte (const int type, const int channel) noexcept
+ {
+ return (uint8) (type | jlimit (0, 15, channel - 1));
+ }
+
+ inline uint8 validVelocity (const int v) noexcept
+ {
+ return (uint8) jlimit (0, 127, v);
+ }
+}
+
+//==============================================================================
+int MidiMessage::readVariableLengthVal (const uint8* data, int& numBytesUsed) noexcept
+{
+ numBytesUsed = 0;
+ int v = 0, i;
+
+ do
+ {
+ i = (int) *data++;
+
+ if (++numBytesUsed > 6)
+ break;
+
+ v = (v << 7) + (i & 0x7f);
+
+ } while (i & 0x80);
+
+ return v;
+}
+
+int MidiMessage::getMessageLengthFromFirstByte (const uint8 firstByte) noexcept
+{
+ // this method only works for valid starting bytes of a short midi message
+ jassert (firstByte >= 0x80 && firstByte != 0xf0 && firstByte != 0xf7);
+
+ static const char messageLengths[] =
+ {
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 1, 2, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ };
+
+ return messageLengths [firstByte & 0x7f];
+}
+
+//==============================================================================
+MidiMessage::MidiMessage() noexcept
+ : timeStamp (0), size (2)
+{
+ preallocatedData.asBytes[0] = 0xf0;
+ preallocatedData.asBytes[1] = 0xf7;
+}
+
+MidiMessage::MidiMessage (const void* const d, const int dataSize, const double t)
+ : timeStamp (t),
+ size (dataSize)
+{
+ jassert (dataSize > 0);
+ memcpy (allocateSpace (dataSize), d, (size_t) dataSize);
+
+ // check that the length matches the data..
+ jassert (size > 3 || *(uint8*)d >= 0xf0 || getMessageLengthFromFirstByte (*(uint8*)d) == size);
+}
+
+MidiMessage::MidiMessage (const int byte1, const double t) noexcept
+ : timeStamp (t), size (1)
+{
+ preallocatedData.asBytes[0] = (uint8) byte1;
+
+ // check that the length matches the data..
+ jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 1);
+}
+
+MidiMessage::MidiMessage (const int byte1, const int byte2, const double t) noexcept
+ : timeStamp (t), size (2)
+{
+ preallocatedData.asBytes[0] = (uint8) byte1;
+ preallocatedData.asBytes[1] = (uint8) byte2;
+
+ // check that the length matches the data..
+ jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 2);
+}
+
+MidiMessage::MidiMessage (const int byte1, const int byte2, const int byte3, const double t) noexcept
+ : timeStamp (t), size (3)
+{
+ preallocatedData.asBytes[0] = (uint8) byte1;
+ preallocatedData.asBytes[1] = (uint8) byte2;
+ preallocatedData.asBytes[2] = (uint8) byte3;
+
+ // check that the length matches the data..
+ jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 3);
+}
+
+MidiMessage::MidiMessage (const MidiMessage& other)
+ : timeStamp (other.timeStamp), size (other.size)
+{
+ if (other.allocatedData != nullptr)
+ {
+ allocatedData.malloc ((size_t) size);
+ memcpy (allocatedData, other.allocatedData, (size_t) size);
+ }
+ else
+ {
+ preallocatedData.asInt32 = other.preallocatedData.asInt32;
+ }
+}
+
+MidiMessage::MidiMessage (const MidiMessage& other, const double newTimeStamp)
+ : timeStamp (newTimeStamp), size (other.size)
+{
+ if (other.allocatedData != nullptr)
+ {
+ allocatedData.malloc ((size_t) size);
+ memcpy (allocatedData, other.allocatedData, (size_t) size);
+ }
+ else
+ {
+ preallocatedData.asInt32 = other.preallocatedData.asInt32;
+ }
+}
+
+MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const uint8 lastStatusByte,
+ double t, bool sysexHasEmbeddedLength)
+ : timeStamp (t)
+{
+ const uint8* src = static_cast (srcData);
+ unsigned int byte = (unsigned int) *src;
+
+ if (byte < 0x80)
+ {
+ byte = (unsigned int) (uint8) lastStatusByte;
+ numBytesUsed = -1;
+ }
+ else
+ {
+ numBytesUsed = 0;
+ --sz;
+ ++src;
+ }
+
+ if (byte >= 0x80)
+ {
+ if (byte == 0xf0)
+ {
+ const uint8* d = src;
+ bool haveReadAllLengthBytes = ! sysexHasEmbeddedLength;
+ int numVariableLengthSysexBytes = 0;
+
+ while (d < src + sz)
+ {
+ if (*d >= 0x80)
+ {
+ if (*d == 0xf7)
+ {
+ ++d; // include the trailing 0xf7 when we hit it
+ break;
+ }
+
+ if (haveReadAllLengthBytes) // if we see a 0x80 bit set after the initial data length
+ break; // bytes, assume it's the end of the sysex
+
+ ++numVariableLengthSysexBytes;
+ }
+ else if (! haveReadAllLengthBytes)
+ {
+ haveReadAllLengthBytes = true;
+ ++numVariableLengthSysexBytes;
+ }
+
+ ++d;
+ }
+
+ src += numVariableLengthSysexBytes;
+ size = 1 + (int) (d - src);
+
+ uint8* dest = allocateSpace (size);
+ *dest = (uint8) byte;
+ memcpy (dest + 1, src, (size_t) (size - 1));
+
+ numBytesUsed += numVariableLengthSysexBytes; // (these aren't counted in the size)
+ }
+ else if (byte == 0xff)
+ {
+ int n;
+ const int bytesLeft = readVariableLengthVal (src + 1, n);
+ size = jmin (sz + 1, n + 2 + bytesLeft);
+
+ uint8* dest = allocateSpace (size);
+ *dest = (uint8) byte;
+ memcpy (dest + 1, src, (size_t) size - 1);
+ }
+ else
+ {
+ preallocatedData.asInt32 = 0;
+ size = getMessageLengthFromFirstByte ((uint8) byte);
+ preallocatedData.asBytes[0] = (uint8) byte;
+
+ if (size > 1)
+ {
+ preallocatedData.asBytes[1] = src[0];
+
+ if (size > 2)
+ preallocatedData.asBytes[2] = src[1];
+ }
+ }
+
+ numBytesUsed += size;
+ }
+ else
+ {
+ preallocatedData.asInt32 = 0;
+ size = 0;
+ }
+}
+
+MidiMessage& MidiMessage::operator= (const MidiMessage& other)
+{
+ if (this != &other)
+ {
+ timeStamp = other.timeStamp;
+ size = other.size;
+
+ if (other.allocatedData != nullptr)
+ {
+ allocatedData.malloc ((size_t) size);
+ memcpy (allocatedData, other.allocatedData, (size_t) size);
+ }
+ else
+ {
+ allocatedData.free();
+ preallocatedData.asInt32 = other.preallocatedData.asInt32;
+ }
+ }
+
+ return *this;
+}
+
+#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
+MidiMessage::MidiMessage (MidiMessage&& other) noexcept
+ : timeStamp (other.timeStamp), size (other.size)
+{
+ if (other.allocatedData != nullptr)
+ allocatedData.swapWith (other.allocatedData);
+ else
+ preallocatedData.asInt32 = other.preallocatedData.asInt32;
+}
+
+MidiMessage& MidiMessage::operator= (MidiMessage&& other) noexcept
+{
+ jassert (this != &other); // shouldn't be possible
+
+ timeStamp = other.timeStamp;
+ size = other.size;
+ allocatedData.swapWith (other.allocatedData);
+ preallocatedData.asInt32 = other.preallocatedData.asInt32;
+
+ return *this;
+}
+#endif
+
+MidiMessage::~MidiMessage() {}
+
+uint8* MidiMessage::allocateSpace (int bytes)
+{
+ if (bytes > 4)
+ {
+ allocatedData.malloc ((size_t) bytes);
+ return allocatedData;
+ }
+
+ return preallocatedData.asBytes;
+}
+
+int MidiMessage::getChannel() const noexcept
+{
+ const uint8* const data = getRawData();
+
+ if ((data[0] & 0xf0) != 0xf0)
+ return (data[0] & 0xf) + 1;
+
+ return 0;
+}
+
+bool MidiMessage::isForChannel (const int channel) const noexcept
+{
+ jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
+
+ const uint8* const data = getRawData();
+
+ return ((data[0] & 0xf) == channel - 1)
+ && ((data[0] & 0xf0) != 0xf0);
+}
+
+void MidiMessage::setChannel (const int channel) noexcept
+{
+ jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
+
+ uint8* const data = getData();
+
+ if ((data[0] & 0xf0) != (uint8) 0xf0)
+ data[0] = (uint8) ((data[0] & (uint8) 0xf0)
+ | (uint8)(channel - 1));
+}
+
+bool MidiMessage::isNoteOn (const bool returnTrueForVelocity0) const noexcept
+{
+ const uint8* const data = getRawData();
+
+ return ((data[0] & 0xf0) == 0x90)
+ && (returnTrueForVelocity0 || data[2] != 0);
+}
+
+bool MidiMessage::isNoteOff (const bool returnTrueForNoteOnVelocity0) const noexcept
+{
+ const uint8* const data = getRawData();
+
+ return ((data[0] & 0xf0) == 0x80)
+ || (returnTrueForNoteOnVelocity0 && (data[2] == 0) && ((data[0] & 0xf0) == 0x90));
+}
+
+bool MidiMessage::isNoteOnOrOff() const noexcept
+{
+ const uint8* const data = getRawData();
+
+ const int d = data[0] & 0xf0;
+ return (d == 0x90) || (d == 0x80);
+}
+
+int MidiMessage::getNoteNumber() const noexcept
+{
+ return getRawData()[1];
+}
+
+void MidiMessage::setNoteNumber (const int newNoteNumber) noexcept
+{
+ if (isNoteOnOrOff())
+ getData()[1] = (uint8) (newNoteNumber & 127);
+}
+
+uint8 MidiMessage::getVelocity() const noexcept
+{
+ if (isNoteOnOrOff())
+ return getRawData()[2];
+
+ return 0;
+}
+
+float MidiMessage::getFloatVelocity() const noexcept
+{
+ return getVelocity() * (1.0f / 127.0f);
+}
+
+void MidiMessage::setVelocity (const float newVelocity) noexcept
+{
+ if (isNoteOnOrOff())
+ getData()[2] = MidiHelpers::validVelocity (roundToInt (newVelocity * 127.0f));
+}
+
+void MidiMessage::multiplyVelocity (const float scaleFactor) noexcept
+{
+ if (isNoteOnOrOff())
+ {
+ uint8* const data = getData();
+ data[2] = MidiHelpers::validVelocity (roundToInt (scaleFactor * data[2]));
+ }
+}
+
+bool MidiMessage::isAftertouch() const noexcept
+{
+ return (getRawData()[0] & 0xf0) == 0xa0;
+}
+
+int MidiMessage::getAfterTouchValue() const noexcept
+{
+ jassert (isAftertouch());
+ return getRawData()[2];
+}
+
+MidiMessage MidiMessage::aftertouchChange (const int channel,
+ const int noteNum,
+ const int aftertouchValue) noexcept
+{
+ jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
+ jassert (isPositiveAndBelow (noteNum, (int) 128));
+ jassert (isPositiveAndBelow (aftertouchValue, (int) 128));
+
+ return MidiMessage (MidiHelpers::initialByte (0xa0, channel),
+ noteNum & 0x7f,
+ aftertouchValue & 0x7f);
+}
+
+bool MidiMessage::isChannelPressure() const noexcept
+{
+ return (getRawData()[0] & 0xf0) == 0xd0;
+}
+
+int MidiMessage::getChannelPressureValue() const noexcept
+{
+ jassert (isChannelPressure());
+ return getRawData()[1];
+}
+
+MidiMessage MidiMessage::channelPressureChange (const int channel, const int pressure) noexcept
+{
+ jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
+ jassert (isPositiveAndBelow (pressure, (int) 128));
+
+ return MidiMessage (MidiHelpers::initialByte (0xd0, channel), pressure & 0x7f);
+}
+
+bool MidiMessage::isSustainPedalOn() const noexcept { return isControllerOfType (0x40) && getRawData()[2] >= 64; }
+bool MidiMessage::isSustainPedalOff() const noexcept { return isControllerOfType (0x40) && getRawData()[2] < 64; }
+
+bool MidiMessage::isSostenutoPedalOn() const noexcept { return isControllerOfType (0x42) && getRawData()[2] >= 64; }
+bool MidiMessage::isSostenutoPedalOff() const noexcept { return isControllerOfType (0x42) && getRawData()[2] < 64; }
+
+bool MidiMessage::isSoftPedalOn() const noexcept { return isControllerOfType (0x43) && getRawData()[2] >= 64; }
+bool MidiMessage::isSoftPedalOff() const noexcept { return isControllerOfType (0x43) && getRawData()[2] < 64; }
+
+
+bool MidiMessage::isProgramChange() const noexcept
+{
+ return (getRawData()[0] & 0xf0) == 0xc0;
+}
+
+int MidiMessage::getProgramChangeNumber() const noexcept
+{
+ jassert (isProgramChange());
+ return getRawData()[1];
+}
+
+MidiMessage MidiMessage::programChange (const int channel, const int programNumber) noexcept
+{
+ jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
+
+ return MidiMessage (MidiHelpers::initialByte (0xc0, channel), programNumber & 0x7f);
+}
+
+bool MidiMessage::isPitchWheel() const noexcept
+{
+ return (getRawData()[0] & 0xf0) == 0xe0;
+}
+
+int MidiMessage::getPitchWheelValue() const noexcept
+{
+ jassert (isPitchWheel());
+ const uint8* const data = getRawData();
+ return data[1] | (data[2] << 7);
+}
+
+MidiMessage MidiMessage::pitchWheel (const int channel, const int position) noexcept
+{
+ jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
+ jassert (isPositiveAndBelow (position, (int) 0x4000));
+
+ return MidiMessage (MidiHelpers::initialByte (0xe0, channel),
+ position & 127, (position >> 7) & 127);
+}
+
+bool MidiMessage::isController() const noexcept
+{
+ return (getRawData()[0] & 0xf0) == 0xb0;
+}
+
+bool MidiMessage::isControllerOfType (const int controllerType) const noexcept
+{
+ const uint8* const data = getRawData();
+ return (data[0] & 0xf0) == 0xb0 && data[1] == controllerType;
+}
+
+int MidiMessage::getControllerNumber() const noexcept
+{
+ jassert (isController());
+ return getRawData()[1];
+}
+
+int MidiMessage::getControllerValue() const noexcept
+{
+ jassert (isController());
+ return getRawData()[2];
+}
+
+MidiMessage MidiMessage::controllerEvent (const int channel, const int controllerType, const int value) noexcept
+{
+ // the channel must be between 1 and 16 inclusive
+ jassert (channel > 0 && channel <= 16);
+
+ return MidiMessage (MidiHelpers::initialByte (0xb0, channel),
+ controllerType & 127, value & 127);
+}
+
+MidiMessage MidiMessage::noteOn (const int channel, const int noteNumber, const float velocity) noexcept
+{
+ return noteOn (channel, noteNumber, (uint8) (velocity * 127.0f + 0.5f));
+}
+
+MidiMessage MidiMessage::noteOn (const int channel, const int noteNumber, const uint8 velocity) noexcept
+{
+ jassert (channel > 0 && channel <= 16);
+ jassert (isPositiveAndBelow (noteNumber, (int) 128));
+
+ return MidiMessage (MidiHelpers::initialByte (0x90, channel),
+ noteNumber & 127, MidiHelpers::validVelocity (velocity));
+}
+
+MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber, uint8 velocity) noexcept
+{
+ jassert (channel > 0 && channel <= 16);
+ jassert (isPositiveAndBelow (noteNumber, (int) 128));
+
+ return MidiMessage (MidiHelpers::initialByte (0x80, channel),
+ noteNumber & 127, MidiHelpers::validVelocity (velocity));
+}
+
+MidiMessage MidiMessage::allNotesOff (const int channel) noexcept
+{
+ return controllerEvent (channel, 123, 0);
+}
+
+bool MidiMessage::isAllNotesOff() const noexcept
+{
+ const uint8* const data = getRawData();
+ return (data[0] & 0xf0) == 0xb0 && data[1] == 123;
+}
+
+MidiMessage MidiMessage::allSoundOff (const int channel) noexcept
+{
+ return controllerEvent (channel, 120, 0);
+}
+
+bool MidiMessage::isAllSoundOff() const noexcept
+{
+ const uint8* const data = getRawData();
+ return (data[0] & 0xf0) == 0xb0 && data[1] == 120;
+}
+
+MidiMessage MidiMessage::allControllersOff (const int channel) noexcept
+{
+ return controllerEvent (channel, 121, 0);
+}
+
+MidiMessage MidiMessage::masterVolume (const float volume)
+{
+ const int vol = jlimit (0, 0x3fff, roundToInt (volume * 0x4000));
+
+ const uint8 buf[] = { 0xf0, 0x7f, 0x7f, 0x04, 0x01,
+ (uint8) (vol & 0x7f),
+ (uint8) (vol >> 7),
+ 0xf7 };
+
+ return MidiMessage (buf, 8);
+}
+
+//==============================================================================
+bool MidiMessage::isSysEx() const noexcept
+{
+ return *getRawData() == 0xf0;
+}
+
+MidiMessage MidiMessage::createSysExMessage (const void* sysexData, const int dataSize)
+{
+ HeapBlock m ((size_t) dataSize + 2);
+
+ m[0] = 0xf0;
+ memcpy (m + 1, sysexData, (size_t) dataSize);
+ m[dataSize + 1] = 0xf7;
+
+ return MidiMessage (m, dataSize + 2);
+}
+
+const uint8* MidiMessage::getSysExData() const noexcept
+{
+ return isSysEx() ? getRawData() + 1 : nullptr;
+}
+
+int MidiMessage::getSysExDataSize() const noexcept
+{
+ return isSysEx() ? size - 2 : 0;
+}
+
+//==============================================================================
+bool MidiMessage::isMetaEvent() const noexcept { return *getRawData() == 0xff; }
+bool MidiMessage::isActiveSense() const noexcept { return *getRawData() == 0xfe; }
+
+int MidiMessage::getMetaEventType() const noexcept
+{
+ const uint8* const data = getRawData();
+ return *data != 0xff ? -1 : data[1];
+}
+
+int MidiMessage::getMetaEventLength() const noexcept
+{
+ const uint8* const data = getRawData();
+ if (*data == 0xff)
+ {
+ int n;
+ return jmin (size - 2, readVariableLengthVal (data + 2, n));
+ }
+
+ return 0;
+}
+
+const uint8* MidiMessage::getMetaEventData() const noexcept
+{
+ jassert (isMetaEvent());
+
+ int n;
+ const uint8* d = getRawData() + 2;
+ readVariableLengthVal (d, n);
+ return d + n;
+}
+
+bool MidiMessage::isTrackMetaEvent() const noexcept { return getMetaEventType() == 0; }
+bool MidiMessage::isEndOfTrackMetaEvent() const noexcept { return getMetaEventType() == 47; }
+
+bool MidiMessage::isTextMetaEvent() const noexcept
+{
+ const int t = getMetaEventType();
+ return t > 0 && t < 16;
+}
+
+String MidiMessage::getTextFromTextMetaEvent() const
+{
+ const char* const textData = reinterpret_cast (getMetaEventData());
+ return String (CharPointer_UTF8 (textData),
+ CharPointer_UTF8 (textData + getMetaEventLength()));
+}
+
+MidiMessage MidiMessage::textMetaEvent (int type, StringRef text)
+{
+ jassert (type > 0 && type < 16);
+
+ MidiMessage result;
+
+ const size_t textSize = text.text.sizeInBytes() - 1;
+
+ uint8 header[8];
+ size_t n = sizeof (header);
+
+ header[--n] = (uint8) (textSize & 0x7f);
+
+ for (size_t i = textSize; (i >>= 7) != 0;)
+ header[--n] = (uint8) ((i & 0x7f) | 0x80);
+
+ header[--n] = (uint8) type;
+ header[--n] = 0xff;
+
+ const size_t headerLen = sizeof (header) - n;
+
+ uint8* const dest = result.allocateSpace ((int) (headerLen + textSize));
+ result.size = (int) (headerLen + textSize);
+
+ memcpy (dest, header + n, headerLen);
+ memcpy (dest + headerLen, text.text.getAddress(), textSize);
+
+ return result;
+}
+
+bool MidiMessage::isTrackNameEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 3) && (*data == 0xff); }
+bool MidiMessage::isTempoMetaEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 81) && (*data == 0xff); }
+bool MidiMessage::isMidiChannelMetaEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 0x20) && (*data == 0xff) && (data[2] == 1); }
+
+int MidiMessage::getMidiChannelMetaEventChannel() const noexcept
+{
+ jassert (isMidiChannelMetaEvent());
+ return getRawData()[3] + 1;
+}
+
+double MidiMessage::getTempoSecondsPerQuarterNote() const noexcept
+{
+ if (! isTempoMetaEvent())
+ return 0.0;
+
+ const uint8* const d = getMetaEventData();
+
+ return (((unsigned int) d[0] << 16)
+ | ((unsigned int) d[1] << 8)
+ | d[2])
+ / 1000000.0;
+}
+
+double MidiMessage::getTempoMetaEventTickLength (const short timeFormat) const noexcept
+{
+ if (timeFormat > 0)
+ {
+ if (! isTempoMetaEvent())
+ return 0.5 / timeFormat;
+
+ return getTempoSecondsPerQuarterNote() / timeFormat;
+ }
+ else
+ {
+ const int frameCode = (-timeFormat) >> 8;
+ double framesPerSecond;
+
+ switch (frameCode)
+ {
+ case 24: framesPerSecond = 24.0; break;
+ case 25: framesPerSecond = 25.0; break;
+ case 29: framesPerSecond = 29.97; break;
+ case 30: framesPerSecond = 30.0; break;
+ default: framesPerSecond = 30.0; break;
+ }
+
+ return (1.0 / framesPerSecond) / (timeFormat & 0xff);
+ }
+}
+
+MidiMessage MidiMessage::tempoMetaEvent (int microsecondsPerQuarterNote) noexcept
+{
+ const uint8 d[] = { 0xff, 81, 3,
+ (uint8) (microsecondsPerQuarterNote >> 16),
+ (uint8) (microsecondsPerQuarterNote >> 8),
+ (uint8) microsecondsPerQuarterNote };
+
+ return MidiMessage (d, 6, 0.0);
+}
+
+bool MidiMessage::isTimeSignatureMetaEvent() const noexcept
+{
+ const uint8* const data = getRawData();
+ return (data[1] == 0x58) && (*data == (uint8) 0xff);
+}
+
+void MidiMessage::getTimeSignatureInfo (int& numerator, int& denominator) const noexcept
+{
+ if (isTimeSignatureMetaEvent())
+ {
+ const uint8* const d = getMetaEventData();
+ numerator = d[0];
+ denominator = 1 << d[1];
+ }
+ else
+ {
+ numerator = 4;
+ denominator = 4;
+ }
+}
+
+MidiMessage MidiMessage::timeSignatureMetaEvent (const int numerator, const int denominator)
+{
+ int n = 1;
+ int powerOfTwo = 0;
+
+ while (n < denominator)
+ {
+ n <<= 1;
+ ++powerOfTwo;
+ }
+
+ const uint8 d[] = { 0xff, 0x58, 0x04, (uint8) numerator, (uint8) powerOfTwo, 1, 96 };
+ return MidiMessage (d, 7, 0.0);
+}
+
+MidiMessage MidiMessage::midiChannelMetaEvent (const int channel) noexcept
+{
+ const uint8 d[] = { 0xff, 0x20, 0x01, (uint8) jlimit (0, 0xff, channel - 1) };
+ return MidiMessage (d, 4, 0.0);
+}
+
+bool MidiMessage::isKeySignatureMetaEvent() const noexcept
+{
+ return getMetaEventType() == 0x59;
+}
+
+int MidiMessage::getKeySignatureNumberOfSharpsOrFlats() const noexcept
+{
+ return (int) getMetaEventData()[0];
+}
+
+bool MidiMessage::isKeySignatureMajorKey() const noexcept
+{
+ return getMetaEventData()[1] == 0;
+}
+
+MidiMessage MidiMessage::keySignatureMetaEvent (int numberOfSharpsOrFlats, bool isMinorKey)
+{
+ jassert (numberOfSharpsOrFlats >= -7 && numberOfSharpsOrFlats <= 7);
+
+ const uint8 d[] = { 0xff, 0x59, 0x02, (uint8) numberOfSharpsOrFlats, isMinorKey ? (uint8) 1 : (uint8) 0 };
+ return MidiMessage (d, 5, 0.0);
+}
+
+MidiMessage MidiMessage::endOfTrack() noexcept
+{
+ return MidiMessage (0xff, 0x2f, 0, 0.0);
+}
+
+//==============================================================================
+bool MidiMessage::isSongPositionPointer() const noexcept { return *getRawData() == 0xf2; }
+int MidiMessage::getSongPositionPointerMidiBeat() const noexcept { const uint8* data = getRawData(); return data[1] | (data[2] << 7); }
+
+MidiMessage MidiMessage::songPositionPointer (const int positionInMidiBeats) noexcept
+{
+ return MidiMessage (0xf2,
+ positionInMidiBeats & 127,
+ (positionInMidiBeats >> 7) & 127);
+}
+
+bool MidiMessage::isMidiStart() const noexcept { return *getRawData() == 0xfa; }
+MidiMessage MidiMessage::midiStart() noexcept { return MidiMessage (0xfa); }
+
+bool MidiMessage::isMidiContinue() const noexcept { return *getRawData() == 0xfb; }
+MidiMessage MidiMessage::midiContinue() noexcept { return MidiMessage (0xfb); }
+
+bool MidiMessage::isMidiStop() const noexcept { return *getRawData() == 0xfc; }
+MidiMessage MidiMessage::midiStop() noexcept { return MidiMessage (0xfc); }
+
+bool MidiMessage::isMidiClock() const noexcept { return *getRawData() == 0xf8; }
+MidiMessage MidiMessage::midiClock() noexcept { return MidiMessage (0xf8); }
+
+bool MidiMessage::isQuarterFrame() const noexcept { return *getRawData() == 0xf1; }
+int MidiMessage::getQuarterFrameSequenceNumber() const noexcept { return ((int) getRawData()[1]) >> 4; }
+int MidiMessage::getQuarterFrameValue() const noexcept { return ((int) getRawData()[1]) & 0x0f; }
+
+MidiMessage MidiMessage::quarterFrame (const int sequenceNumber, const int value) noexcept
+{
+ return MidiMessage (0xf1, (sequenceNumber << 4) | value);
+}
+
+bool MidiMessage::isFullFrame() const noexcept
+{
+ const uint8* const data = getRawData();
+
+ return data[0] == 0xf0
+ && data[1] == 0x7f
+ && size >= 10
+ && data[3] == 0x01
+ && data[4] == 0x01;
+}
+
+void MidiMessage::getFullFrameParameters (int& hours, int& minutes, int& seconds, int& frames,
+ MidiMessage::SmpteTimecodeType& timecodeType) const noexcept
+{
+ jassert (isFullFrame());
+
+ const uint8* const data = getRawData();
+ timecodeType = (SmpteTimecodeType) (data[5] >> 5);
+ hours = data[5] & 0x1f;
+ minutes = data[6];
+ seconds = data[7];
+ frames = data[8];
+}
+
+MidiMessage MidiMessage::fullFrame (const int hours, const int minutes,
+ const int seconds, const int frames,
+ MidiMessage::SmpteTimecodeType timecodeType)
+{
+ const uint8 d[] = { 0xf0, 0x7f, 0x7f, 0x01, 0x01,
+ (uint8) ((hours & 0x01f) | (timecodeType << 5)),
+ (uint8) minutes,
+ (uint8) seconds,
+ (uint8) frames,
+ 0xf7 };
+
+ return MidiMessage (d, 10, 0.0);
+}
+
+bool MidiMessage::isMidiMachineControlMessage() const noexcept
+{
+ const uint8* const data = getRawData();
+ return data[0] == 0xf0
+ && data[1] == 0x7f
+ && data[3] == 0x06
+ && size > 5;
+}
+
+MidiMessage::MidiMachineControlCommand MidiMessage::getMidiMachineControlCommand() const noexcept
+{
+ jassert (isMidiMachineControlMessage());
+
+ return (MidiMachineControlCommand) getRawData()[4];
+}
+
+MidiMessage MidiMessage::midiMachineControlCommand (MidiMessage::MidiMachineControlCommand command)
+{
+ const uint8 d[] = { 0xf0, 0x7f, 0, 6, (uint8) command, 0xf7 };
+
+ return MidiMessage (d, 6, 0.0);
+}
+
+//==============================================================================
+bool MidiMessage::isMidiMachineControlGoto (int& hours, int& minutes, int& seconds, int& frames) const noexcept
+{
+ const uint8* const data = getRawData();
+ if (size >= 12
+ && data[0] == 0xf0
+ && data[1] == 0x7f
+ && data[3] == 0x06
+ && data[4] == 0x44
+ && data[5] == 0x06
+ && data[6] == 0x01)
+ {
+ hours = data[7] % 24; // (that some machines send out hours > 24)
+ minutes = data[8];
+ seconds = data[9];
+ frames = data[10];
+
+ return true;
+ }
+
+ return false;
+}
+
+MidiMessage MidiMessage::midiMachineControlGoto (int hours, int minutes, int seconds, int frames)
+{
+ const uint8 d[] = { 0xf0, 0x7f, 0, 6, 0x44, 6, 1,
+ (uint8) hours,
+ (uint8) minutes,
+ (uint8) seconds,
+ (uint8) frames,
+ 0xf7 };
+
+ return MidiMessage (d, 12, 0.0);
+}
+
+//==============================================================================
+String MidiMessage::getMidiNoteName (int note, bool useSharps, bool includeOctaveNumber, int octaveNumForMiddleC)
+{
+ static const char* const sharpNoteNames[] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" };
+ static const char* const flatNoteNames[] = { "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B" };
+
+ if (isPositiveAndBelow (note, (int) 128))
+ {
+ String s (useSharps ? sharpNoteNames [note % 12]
+ : flatNoteNames [note % 12]);
+
+ if (includeOctaveNumber)
+ s << (note / 12 + (octaveNumForMiddleC - 5));
+
+ return s;
+ }
+
+ return String::empty;
+}
+
+double MidiMessage::getMidiNoteInHertz (int noteNumber, const double frequencyOfA) noexcept
+{
+ return frequencyOfA * pow (2.0, (noteNumber - 69) / 12.0);
+}
+
+bool MidiMessage::isMidiNoteBlack (int noteNumber) noexcept
+{
+ return ((1 << (noteNumber % 12)) & 0x054a) != 0;
+}
+
+const char* MidiMessage::getGMInstrumentName (const int n)
+{
+ static const char* names[] =
+ {
+ NEEDS_TRANS("Acoustic Grand Piano"), NEEDS_TRANS("Bright Acoustic Piano"), NEEDS_TRANS("Electric Grand Piano"), NEEDS_TRANS("Honky-tonk Piano"),
+ NEEDS_TRANS("Electric Piano 1"), NEEDS_TRANS("Electric Piano 2"), NEEDS_TRANS("Harpsichord"), NEEDS_TRANS("Clavinet"),
+ NEEDS_TRANS("Celesta"), NEEDS_TRANS("Glockenspiel"), NEEDS_TRANS("Music Box"), NEEDS_TRANS("Vibraphone"),
+ NEEDS_TRANS("Marimba"), NEEDS_TRANS("Xylophone"), NEEDS_TRANS("Tubular Bells"), NEEDS_TRANS("Dulcimer"),
+ NEEDS_TRANS("Drawbar Organ"), NEEDS_TRANS("Percussive Organ"), NEEDS_TRANS("Rock Organ"), NEEDS_TRANS("Church Organ"),
+ NEEDS_TRANS("Reed Organ"), NEEDS_TRANS("Accordion"), NEEDS_TRANS("Harmonica"), NEEDS_TRANS("Tango Accordion"),
+ NEEDS_TRANS("Acoustic Guitar (nylon)"), NEEDS_TRANS("Acoustic Guitar (steel)"), NEEDS_TRANS("Electric Guitar (jazz)"), NEEDS_TRANS("Electric Guitar (clean)"),
+ NEEDS_TRANS("Electric Guitar (mute)"), NEEDS_TRANS("Overdriven Guitar"), NEEDS_TRANS("Distortion Guitar"), NEEDS_TRANS("Guitar Harmonics"),
+ NEEDS_TRANS("Acoustic Bass"), NEEDS_TRANS("Electric Bass (finger)"), NEEDS_TRANS("Electric Bass (pick)"), NEEDS_TRANS("Fretless Bass"),
+ NEEDS_TRANS("Slap Bass 1"), NEEDS_TRANS("Slap Bass 2"), NEEDS_TRANS("Synth Bass 1"), NEEDS_TRANS("Synth Bass 2"),
+ NEEDS_TRANS("Violin"), NEEDS_TRANS("Viola"), NEEDS_TRANS("Cello"), NEEDS_TRANS("Contrabass"),
+ NEEDS_TRANS("Tremolo Strings"), NEEDS_TRANS("Pizzicato Strings"), NEEDS_TRANS("Orchestral Harp"), NEEDS_TRANS("Timpani"),
+ NEEDS_TRANS("String Ensemble 1"), NEEDS_TRANS("String Ensemble 2"), NEEDS_TRANS("SynthStrings 1"), NEEDS_TRANS("SynthStrings 2"),
+ NEEDS_TRANS("Choir Aahs"), NEEDS_TRANS("Voice Oohs"), NEEDS_TRANS("Synth Voice"), NEEDS_TRANS("Orchestra Hit"),
+ NEEDS_TRANS("Trumpet"), NEEDS_TRANS("Trombone"), NEEDS_TRANS("Tuba"), NEEDS_TRANS("Muted Trumpet"),
+ NEEDS_TRANS("French Horn"), NEEDS_TRANS("Brass Section"), NEEDS_TRANS("SynthBrass 1"), NEEDS_TRANS("SynthBrass 2"),
+ NEEDS_TRANS("Soprano Sax"), NEEDS_TRANS("Alto Sax"), NEEDS_TRANS("Tenor Sax"), NEEDS_TRANS("Baritone Sax"),
+ NEEDS_TRANS("Oboe"), NEEDS_TRANS("English Horn"), NEEDS_TRANS("Bassoon"), NEEDS_TRANS("Clarinet"),
+ NEEDS_TRANS("Piccolo"), NEEDS_TRANS("Flute"), NEEDS_TRANS("Recorder"), NEEDS_TRANS("Pan Flute"),
+ NEEDS_TRANS("Blown Bottle"), NEEDS_TRANS("Shakuhachi"), NEEDS_TRANS("Whistle"), NEEDS_TRANS("Ocarina"),
+ NEEDS_TRANS("Lead 1 (square)"), NEEDS_TRANS("Lead 2 (sawtooth)"), NEEDS_TRANS("Lead 3 (calliope)"), NEEDS_TRANS("Lead 4 (chiff)"),
+ NEEDS_TRANS("Lead 5 (charang)"), NEEDS_TRANS("Lead 6 (voice)"), NEEDS_TRANS("Lead 7 (fifths)"), NEEDS_TRANS("Lead 8 (bass+lead)"),
+ NEEDS_TRANS("Pad 1 (new age)"), NEEDS_TRANS("Pad 2 (warm)"), NEEDS_TRANS("Pad 3 (polysynth)"), NEEDS_TRANS("Pad 4 (choir)"),
+ NEEDS_TRANS("Pad 5 (bowed)"), NEEDS_TRANS("Pad 6 (metallic)"), NEEDS_TRANS("Pad 7 (halo)"), NEEDS_TRANS("Pad 8 (sweep)"),
+ NEEDS_TRANS("FX 1 (rain)"), NEEDS_TRANS("FX 2 (soundtrack)"), NEEDS_TRANS("FX 3 (crystal)"), NEEDS_TRANS("FX 4 (atmosphere)"),
+ NEEDS_TRANS("FX 5 (brightness)"), NEEDS_TRANS("FX 6 (goblins)"), NEEDS_TRANS("FX 7 (echoes)"), NEEDS_TRANS("FX 8 (sci-fi)"),
+ NEEDS_TRANS("Sitar"), NEEDS_TRANS("Banjo"), NEEDS_TRANS("Shamisen"), NEEDS_TRANS("Koto"),
+ NEEDS_TRANS("Kalimba"), NEEDS_TRANS("Bag pipe"), NEEDS_TRANS("Fiddle"), NEEDS_TRANS("Shanai"),
+ NEEDS_TRANS("Tinkle Bell"), NEEDS_TRANS("Agogo"), NEEDS_TRANS("Steel Drums"), NEEDS_TRANS("Woodblock"),
+ NEEDS_TRANS("Taiko Drum"), NEEDS_TRANS("Melodic Tom"), NEEDS_TRANS("Synth Drum"), NEEDS_TRANS("Reverse Cymbal"),
+ NEEDS_TRANS("Guitar Fret Noise"), NEEDS_TRANS("Breath Noise"), NEEDS_TRANS("Seashore"), NEEDS_TRANS("Bird Tweet"),
+ NEEDS_TRANS("Telephone Ring"), NEEDS_TRANS("Helicopter"), NEEDS_TRANS("Applause"), NEEDS_TRANS("Gunshot")
+ };
+
+ return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr;
+}
+
+const char* MidiMessage::getGMInstrumentBankName (const int n)
+{
+ static const char* names[] =
+ {
+ NEEDS_TRANS("Piano"), NEEDS_TRANS("Chromatic Percussion"), NEEDS_TRANS("Organ"), NEEDS_TRANS("Guitar"),
+ NEEDS_TRANS("Bass"), NEEDS_TRANS("Strings"), NEEDS_TRANS("Ensemble"), NEEDS_TRANS("Brass"),
+ NEEDS_TRANS("Reed"), NEEDS_TRANS("Pipe"), NEEDS_TRANS("Synth Lead"), NEEDS_TRANS("Synth Pad"),
+ NEEDS_TRANS("Synth Effects"), NEEDS_TRANS("Ethnic"), NEEDS_TRANS("Percussive"), NEEDS_TRANS("Sound Effects")
+ };
+
+ return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr;
+}
+
+const char* MidiMessage::getRhythmInstrumentName (const int n)
+{
+ static const char* names[] =
+ {
+ NEEDS_TRANS("Acoustic Bass Drum"), NEEDS_TRANS("Bass Drum 1"), NEEDS_TRANS("Side Stick"), NEEDS_TRANS("Acoustic Snare"),
+ NEEDS_TRANS("Hand Clap"), NEEDS_TRANS("Electric Snare"), NEEDS_TRANS("Low Floor Tom"), NEEDS_TRANS("Closed Hi-Hat"),
+ NEEDS_TRANS("High Floor Tom"), NEEDS_TRANS("Pedal Hi-Hat"), NEEDS_TRANS("Low Tom"), NEEDS_TRANS("Open Hi-Hat"),
+ NEEDS_TRANS("Low-Mid Tom"), NEEDS_TRANS("Hi-Mid Tom"), NEEDS_TRANS("Crash Cymbal 1"), NEEDS_TRANS("High Tom"),
+ NEEDS_TRANS("Ride Cymbal 1"), NEEDS_TRANS("Chinese Cymbal"), NEEDS_TRANS("Ride Bell"), NEEDS_TRANS("Tambourine"),
+ NEEDS_TRANS("Splash Cymbal"), NEEDS_TRANS("Cowbell"), NEEDS_TRANS("Crash Cymbal 2"), NEEDS_TRANS("Vibraslap"),
+ NEEDS_TRANS("Ride Cymbal 2"), NEEDS_TRANS("Hi Bongo"), NEEDS_TRANS("Low Bongo"), NEEDS_TRANS("Mute Hi Conga"),
+ NEEDS_TRANS("Open Hi Conga"), NEEDS_TRANS("Low Conga"), NEEDS_TRANS("High Timbale"), NEEDS_TRANS("Low Timbale"),
+ NEEDS_TRANS("High Agogo"), NEEDS_TRANS("Low Agogo"), NEEDS_TRANS("Cabasa"), NEEDS_TRANS("Maracas"),
+ NEEDS_TRANS("Short Whistle"), NEEDS_TRANS("Long Whistle"), NEEDS_TRANS("Short Guiro"), NEEDS_TRANS("Long Guiro"),
+ NEEDS_TRANS("Claves"), NEEDS_TRANS("Hi Wood Block"), NEEDS_TRANS("Low Wood Block"), NEEDS_TRANS("Mute Cuica"),
+ NEEDS_TRANS("Open Cuica"), NEEDS_TRANS("Mute Triangle"), NEEDS_TRANS("Open Triangle")
+ };
+
+ return (n >= 35 && n <= 81) ? names [n - 35] : nullptr;
+}
+
+const char* MidiMessage::getControllerName (const int n)
+{
+ static const char* names[] =
+ {
+ NEEDS_TRANS("Bank Select"), NEEDS_TRANS("Modulation Wheel (coarse)"), NEEDS_TRANS("Breath controller (coarse)"),
+ nullptr,
+ NEEDS_TRANS("Foot Pedal (coarse)"), NEEDS_TRANS("Portamento Time (coarse)"), NEEDS_TRANS("Data Entry (coarse)"),
+ NEEDS_TRANS("Volume (coarse)"), NEEDS_TRANS("Balance (coarse)"),
+ nullptr,
+ NEEDS_TRANS("Pan position (coarse)"), NEEDS_TRANS("Expression (coarse)"), NEEDS_TRANS("Effect Control 1 (coarse)"),
+ NEEDS_TRANS("Effect Control 2 (coarse)"),
+ nullptr, nullptr,
+ NEEDS_TRANS("General Purpose Slider 1"), NEEDS_TRANS("General Purpose Slider 2"),
+ NEEDS_TRANS("General Purpose Slider 3"), NEEDS_TRANS("General Purpose Slider 4"),
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ NEEDS_TRANS("Bank Select (fine)"), NEEDS_TRANS("Modulation Wheel (fine)"), NEEDS_TRANS("Breath controller (fine)"),
+ nullptr,
+ NEEDS_TRANS("Foot Pedal (fine)"), NEEDS_TRANS("Portamento Time (fine)"), NEEDS_TRANS("Data Entry (fine)"), NEEDS_TRANS("Volume (fine)"),
+ NEEDS_TRANS("Balance (fine)"), nullptr, NEEDS_TRANS("Pan position (fine)"), NEEDS_TRANS("Expression (fine)"),
+ NEEDS_TRANS("Effect Control 1 (fine)"), NEEDS_TRANS("Effect Control 2 (fine)"),
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ NEEDS_TRANS("Hold Pedal (on/off)"), NEEDS_TRANS("Portamento (on/off)"), NEEDS_TRANS("Sustenuto Pedal (on/off)"), NEEDS_TRANS("Soft Pedal (on/off)"),
+ NEEDS_TRANS("Legato Pedal (on/off)"), NEEDS_TRANS("Hold 2 Pedal (on/off)"), NEEDS_TRANS("Sound Variation"), NEEDS_TRANS("Sound Timbre"),
+ NEEDS_TRANS("Sound Release Time"), NEEDS_TRANS("Sound Attack Time"), NEEDS_TRANS("Sound Brightness"), NEEDS_TRANS("Sound Control 6"),
+ NEEDS_TRANS("Sound Control 7"), NEEDS_TRANS("Sound Control 8"), NEEDS_TRANS("Sound Control 9"), NEEDS_TRANS("Sound Control 10"),
+ NEEDS_TRANS("General Purpose Button 1 (on/off)"), NEEDS_TRANS("General Purpose Button 2 (on/off)"),
+ NEEDS_TRANS("General Purpose Button 3 (on/off)"), NEEDS_TRANS("General Purpose Button 4 (on/off)"),
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ NEEDS_TRANS("Reverb Level"), NEEDS_TRANS("Tremolo Level"), NEEDS_TRANS("Chorus Level"), NEEDS_TRANS("Celeste Level"),
+ NEEDS_TRANS("Phaser Level"), NEEDS_TRANS("Data Button increment"), NEEDS_TRANS("Data Button decrement"), NEEDS_TRANS("Non-registered Parameter (fine)"),
+ NEEDS_TRANS("Non-registered Parameter (coarse)"), NEEDS_TRANS("Registered Parameter (fine)"), NEEDS_TRANS("Registered Parameter (coarse)"),
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ NEEDS_TRANS("All Sound Off"), NEEDS_TRANS("All Controllers Off"), NEEDS_TRANS("Local Keyboard (on/off)"), NEEDS_TRANS("All Notes Off"),
+ NEEDS_TRANS("Omni Mode Off"), NEEDS_TRANS("Omni Mode On"), NEEDS_TRANS("Mono Operation"), NEEDS_TRANS("Poly Operation")
+ };
+
+ return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr;
+}
diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.h b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.h
new file mode 100644
index 0000000..9002aa0
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.h
@@ -0,0 +1,903 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_MIDIMESSAGE_H_INCLUDED
+#define JUCE_MIDIMESSAGE_H_INCLUDED
+
+
+//==============================================================================
+/**
+ Encapsulates a MIDI message.
+
+ @see MidiMessageSequence, MidiOutput, MidiInput
+*/
+class JUCE_API MidiMessage
+{
+public:
+ //==============================================================================
+ /** Creates a 3-byte short midi message.
+
+ @param byte1 message byte 1
+ @param byte2 message byte 2
+ @param byte3 message byte 3
+ @param timeStamp the time to give the midi message - this value doesn't
+ use any particular units, so will be application-specific
+ */
+ MidiMessage (int byte1, int byte2, int byte3, double timeStamp = 0) noexcept;
+
+ /** Creates a 2-byte short midi message.
+
+ @param byte1 message byte 1
+ @param byte2 message byte 2
+ @param timeStamp the time to give the midi message - this value doesn't
+ use any particular units, so will be application-specific
+ */
+ MidiMessage (int byte1, int byte2, double timeStamp = 0) noexcept;
+
+ /** Creates a 1-byte short midi message.
+
+ @param byte1 message byte 1
+ @param timeStamp the time to give the midi message - this value doesn't
+ use any particular units, so will be application-specific
+ */
+ MidiMessage (int byte1, double timeStamp = 0) noexcept;
+
+ /** Creates a midi message from a block of data. */
+ MidiMessage (const void* data, int numBytes, double timeStamp = 0);
+
+ /** Reads the next midi message from some data.
+
+ This will read as many bytes from a data stream as it needs to make a
+ complete message, and will return the number of bytes it used. This lets
+ you read a sequence of midi messages from a file or stream.
+
+ @param data the data to read from
+ @param maxBytesToUse the maximum number of bytes it's allowed to read
+ @param numBytesUsed returns the number of bytes that were actually needed
+ @param lastStatusByte in a sequence of midi messages, the initial byte
+ can be dropped from a message if it's the same as the
+ first byte of the previous message, so this lets you
+ supply the byte to use if the first byte of the message
+ has in fact been dropped.
+ @param timeStamp the time to give the midi message - this value doesn't
+ use any particular units, so will be application-specific
+ @param sysexHasEmbeddedLength when reading sysexes, this flag indicates whether
+ to expect the data to begin with a variable-length field
+ indicating its size
+ */
+ MidiMessage (const void* data, int maxBytesToUse,
+ int& numBytesUsed, uint8 lastStatusByte,
+ double timeStamp = 0,
+ bool sysexHasEmbeddedLength = true);
+
+ /** Creates an active-sense message.
+ Since the MidiMessage has to contain a valid message, this default constructor
+ just initialises it with an empty sysex message.
+ */
+ MidiMessage() noexcept;
+
+ /** Creates a copy of another midi message. */
+ MidiMessage (const MidiMessage&);
+
+ /** Creates a copy of another midi message, with a different timestamp. */
+ MidiMessage (const MidiMessage&, double newTimeStamp);
+
+ /** Destructor. */
+ ~MidiMessage();
+
+ /** Copies this message from another one. */
+ MidiMessage& operator= (const MidiMessage& other);
+
+ #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
+ MidiMessage (MidiMessage&&) noexcept;
+ MidiMessage& operator= (MidiMessage&&) noexcept;
+ #endif
+
+ //==============================================================================
+ /** Returns a pointer to the raw midi data.
+ @see getRawDataSize
+ */
+ const uint8* getRawData() const noexcept { return allocatedData != nullptr ? allocatedData.getData() : preallocatedData.asBytes; }
+
+ /** Returns the number of bytes of data in the message.
+ @see getRawData
+ */
+ int getRawDataSize() const noexcept { return size; }
+
+ //==============================================================================
+ /** Returns the timestamp associated with this message.
+
+ The exact meaning of this time and its units will vary, as messages are used in
+ a variety of different contexts.
+
+ If you're getting the message from a midi file, this could be a time in seconds, or
+ a number of ticks - see MidiFile::convertTimestampTicksToSeconds().
+
+ If the message is being used in a MidiBuffer, it might indicate the number of
+ audio samples from the start of the buffer.
+
+ If the message was created by a MidiInput, see MidiInputCallback::handleIncomingMidiMessage()
+ for details of the way that it initialises this value.
+
+ @see setTimeStamp, addToTimeStamp
+ */
+ double getTimeStamp() const noexcept { return timeStamp; }
+
+ /** Changes the message's associated timestamp.
+ The units for the timestamp will be application-specific - see the notes for getTimeStamp().
+ @see addToTimeStamp, getTimeStamp
+ */
+ void setTimeStamp (double newTimestamp) noexcept { timeStamp = newTimestamp; }
+
+ /** Adds a value to the message's timestamp.
+ The units for the timestamp will be application-specific.
+ */
+ void addToTimeStamp (double delta) noexcept { timeStamp += delta; }
+
+ //==============================================================================
+ /** Returns the midi channel associated with the message.
+
+ @returns a value 1 to 16 if the message has a channel, or 0 if it hasn't (e.g.
+ if it's a sysex)
+ @see isForChannel, setChannel
+ */
+ int getChannel() const noexcept;
+
+ /** Returns true if the message applies to the given midi channel.
+
+ @param channelNumber the channel number to look for, in the range 1 to 16
+ @see getChannel, setChannel
+ */
+ bool isForChannel (int channelNumber) const noexcept;
+
+ /** Changes the message's midi channel.
+ This won't do anything for non-channel messages like sysexes.
+ @param newChannelNumber the channel number to change it to, in the range 1 to 16
+ */
+ void setChannel (int newChannelNumber) noexcept;
+
+ //==============================================================================
+ /** Returns true if this is a system-exclusive message.
+ */
+ bool isSysEx() const noexcept;
+
+ /** Returns a pointer to the sysex data inside the message.
+ If this event isn't a sysex event, it'll return 0.
+ @see getSysExDataSize
+ */
+ const uint8* getSysExData() const noexcept;
+
+ /** Returns the size of the sysex data.
+ This value excludes the 0xf0 header byte and the 0xf7 at the end.
+ @see getSysExData
+ */
+ int getSysExDataSize() const noexcept;
+
+ //==============================================================================
+ /** Returns true if this message is a 'key-down' event.
+
+ @param returnTrueForVelocity0 if true, then if this event is a note-on with
+ velocity 0, it will still be considered to be a note-on and the
+ method will return true. If returnTrueForVelocity0 is false, then
+ if this is a note-on event with velocity 0, it'll be regarded as
+ a note-off, and the method will return false
+
+ @see isNoteOff, getNoteNumber, getVelocity, noteOn
+ */
+ bool isNoteOn (bool returnTrueForVelocity0 = false) const noexcept;
+
+ /** Creates a key-down message (using a floating-point velocity).
+
+ @param channel the midi channel, in the range 1 to 16
+ @param noteNumber the key number, 0 to 127
+ @param velocity in the range 0 to 1.0
+ @see isNoteOn
+ */
+ static MidiMessage noteOn (int channel, int noteNumber, float velocity) noexcept;
+
+ /** Creates a key-down message (using an integer velocity).
+
+ @param channel the midi channel, in the range 1 to 16
+ @param noteNumber the key number, 0 to 127
+ @param velocity in the range 0 to 127
+ @see isNoteOn
+ */
+ static MidiMessage noteOn (int channel, int noteNumber, uint8 velocity) noexcept;
+
+ /** Returns true if this message is a 'key-up' event.
+
+ If returnTrueForNoteOnVelocity0 is true, then his will also return true
+ for a note-on event with a velocity of 0.
+
+ @see isNoteOn, getNoteNumber, getVelocity, noteOff
+ */
+ bool isNoteOff (bool returnTrueForNoteOnVelocity0 = true) const noexcept;
+
+ /** Creates a key-up message.
+
+ @param channel the midi channel, in the range 1 to 16
+ @param noteNumber the key number, 0 to 127
+ @param velocity in the range 0 to 127
+ @see isNoteOff
+ */
+ static MidiMessage noteOff (int channel, int noteNumber, uint8 velocity = 0) noexcept;
+
+ /** Returns true if this message is a 'key-down' or 'key-up' event.
+
+ @see isNoteOn, isNoteOff
+ */
+ bool isNoteOnOrOff() const noexcept;
+
+ /** Returns the midi note number for note-on and note-off messages.
+ If the message isn't a note-on or off, the value returned is undefined.
+ @see isNoteOff, getMidiNoteName, getMidiNoteInHertz, setNoteNumber
+ */
+ int getNoteNumber() const noexcept;
+
+ /** Changes the midi note number of a note-on or note-off message.
+ If the message isn't a note on or off, this will do nothing.
+ */
+ void setNoteNumber (int newNoteNumber) noexcept;
+
+ //==============================================================================
+ /** Returns the velocity of a note-on or note-off message.
+
+ The value returned will be in the range 0 to 127.
+ If the message isn't a note-on or off event, it will return 0.
+
+ @see getFloatVelocity
+ */
+ uint8 getVelocity() const noexcept;
+
+ /** Returns the velocity of a note-on or note-off message.
+
+ The value returned will be in the range 0 to 1.0
+ If the message isn't a note-on or off event, it will return 0.
+
+ @see getVelocity, setVelocity
+ */
+ float getFloatVelocity() const noexcept;
+
+ /** Changes the velocity of a note-on or note-off message.
+
+ If the message isn't a note on or off, this will do nothing.
+
+ @param newVelocity the new velocity, in the range 0 to 1.0
+ @see getFloatVelocity, multiplyVelocity
+ */
+ void setVelocity (float newVelocity) noexcept;
+
+ /** Multiplies the velocity of a note-on or note-off message by a given amount.
+
+ If the message isn't a note on or off, this will do nothing.
+
+ @param scaleFactor the value by which to multiply the velocity
+ @see setVelocity
+ */
+ void multiplyVelocity (float scaleFactor) noexcept;
+
+ //==============================================================================
+ /** Returns true if this message is a 'sustain pedal down' controller message. */
+ bool isSustainPedalOn() const noexcept;
+ /** Returns true if this message is a 'sustain pedal up' controller message. */
+ bool isSustainPedalOff() const noexcept;
+
+ /** Returns true if this message is a 'sostenuto pedal down' controller message. */
+ bool isSostenutoPedalOn() const noexcept;
+ /** Returns true if this message is a 'sostenuto pedal up' controller message. */
+ bool isSostenutoPedalOff() const noexcept;
+
+ /** Returns true if this message is a 'soft pedal down' controller message. */
+ bool isSoftPedalOn() const noexcept;
+ /** Returns true if this message is a 'soft pedal up' controller message. */
+ bool isSoftPedalOff() const noexcept;
+
+ //==============================================================================
+ /** Returns true if the message is a program (patch) change message.
+ @see getProgramChangeNumber, getGMInstrumentName
+ */
+ bool isProgramChange() const noexcept;
+
+ /** Returns the new program number of a program change message.
+ If the message isn't a program change, the value returned is undefined.
+ @see isProgramChange, getGMInstrumentName
+ */
+ int getProgramChangeNumber() const noexcept;
+
+ /** Creates a program-change message.
+
+ @param channel the midi channel, in the range 1 to 16
+ @param programNumber the midi program number, 0 to 127
+ @see isProgramChange, getGMInstrumentName
+ */
+ static MidiMessage programChange (int channel, int programNumber) noexcept;
+
+ //==============================================================================
+ /** Returns true if the message is a pitch-wheel move.
+ @see getPitchWheelValue, pitchWheel
+ */
+ bool isPitchWheel() const noexcept;
+
+ /** Returns the pitch wheel position from a pitch-wheel move message.
+
+ The value returned is a 14-bit number from 0 to 0x3fff, indicating the wheel position.
+ If called for messages which aren't pitch wheel events, the number returned will be
+ nonsense.
+
+ @see isPitchWheel
+ */
+ int getPitchWheelValue() const noexcept;
+
+ /** Creates a pitch-wheel move message.
+
+ @param channel the midi channel, in the range 1 to 16
+ @param position the wheel position, in the range 0 to 16383
+ @see isPitchWheel
+ */
+ static MidiMessage pitchWheel (int channel, int position) noexcept;
+
+ //==============================================================================
+ /** Returns true if the message is an aftertouch event.
+
+ For aftertouch events, use the getNoteNumber() method to find out the key
+ that it applies to, and getAftertouchValue() to find out the amount. Use
+ getChannel() to find out the channel.
+
+ @see getAftertouchValue, getNoteNumber
+ */
+ bool isAftertouch() const noexcept;
+
+ /** Returns the amount of aftertouch from an aftertouch messages.
+
+ The value returned is in the range 0 to 127, and will be nonsense for messages
+ other than aftertouch messages.
+
+ @see isAftertouch
+ */
+ int getAfterTouchValue() const noexcept;
+
+ /** Creates an aftertouch message.
+
+ @param channel the midi channel, in the range 1 to 16
+ @param noteNumber the key number, 0 to 127
+ @param aftertouchAmount the amount of aftertouch, 0 to 127
+ @see isAftertouch
+ */
+ static MidiMessage aftertouchChange (int channel,
+ int noteNumber,
+ int aftertouchAmount) noexcept;
+
+ /** Returns true if the message is a channel-pressure change event.
+
+ This is like aftertouch, but common to the whole channel rather than a specific
+ note. Use getChannelPressureValue() to find out the pressure, and getChannel()
+ to find out the channel.
+
+ @see channelPressureChange
+ */
+ bool isChannelPressure() const noexcept;
+
+ /** Returns the pressure from a channel pressure change message.
+
+ @returns the pressure, in the range 0 to 127
+ @see isChannelPressure, channelPressureChange
+ */
+ int getChannelPressureValue() const noexcept;
+
+ /** Creates a channel-pressure change event.
+
+ @param channel the midi channel: 1 to 16
+ @param pressure the pressure, 0 to 127
+ @see isChannelPressure
+ */
+ static MidiMessage channelPressureChange (int channel, int pressure) noexcept;
+
+ //==============================================================================
+ /** Returns true if this is a midi controller message.
+
+ @see getControllerNumber, getControllerValue, controllerEvent
+ */
+ bool isController() const noexcept;
+
+ /** Returns the controller number of a controller message.
+
+ The name of the controller can be looked up using the getControllerName() method.
+ Note that the value returned is invalid for messages that aren't controller changes.
+
+ @see isController, getControllerName, getControllerValue
+ */
+ int getControllerNumber() const noexcept;
+
+ /** Returns the controller value from a controller message.
+
+ A value 0 to 127 is returned to indicate the new controller position.
+ Note that the value returned is invalid for messages that aren't controller changes.
+
+ @see isController, getControllerNumber
+ */
+ int getControllerValue() const noexcept;
+
+ /** Returns true if this message is a controller message and if it has the specified
+ controller type.
+ */
+ bool isControllerOfType (int controllerType) const noexcept;
+
+ /** Creates a controller message.
+
+ @param channel the midi channel, in the range 1 to 16
+ @param controllerType the type of controller
+ @param value the controller value
+ @see isController
+ */
+ static MidiMessage controllerEvent (int channel,
+ int controllerType,
+ int value) noexcept;
+
+ /** Checks whether this message is an all-notes-off message.
+ @see allNotesOff
+ */
+ bool isAllNotesOff() const noexcept;
+
+ /** Checks whether this message is an all-sound-off message.
+ @see allSoundOff
+ */
+ bool isAllSoundOff() const noexcept;
+
+ /** Creates an all-notes-off message.
+
+ @param channel the midi channel, in the range 1 to 16
+ @see isAllNotesOff
+ */
+ static MidiMessage allNotesOff (int channel) noexcept;
+
+ /** Creates an all-sound-off message.
+
+ @param channel the midi channel, in the range 1 to 16
+ @see isAllSoundOff
+ */
+ static MidiMessage allSoundOff (int channel) noexcept;
+
+ /** Creates an all-controllers-off message.
+
+ @param channel the midi channel, in the range 1 to 16
+ */
+ static MidiMessage allControllersOff (int channel) noexcept;
+
+ //==============================================================================
+ /** Returns true if this event is a meta-event.
+
+ Meta-events are things like tempo changes, track names, etc.
+
+ @see getMetaEventType, isTrackMetaEvent, isEndOfTrackMetaEvent,
+ isTextMetaEvent, isTrackNameEvent, isTempoMetaEvent, isTimeSignatureMetaEvent,
+ isKeySignatureMetaEvent, isMidiChannelMetaEvent
+ */
+ bool isMetaEvent() const noexcept;
+
+ /** Returns a meta-event's type number.
+
+ If the message isn't a meta-event, this will return -1.
+
+ @see isMetaEvent, isTrackMetaEvent, isEndOfTrackMetaEvent,
+ isTextMetaEvent, isTrackNameEvent, isTempoMetaEvent, isTimeSignatureMetaEvent,
+ isKeySignatureMetaEvent, isMidiChannelMetaEvent
+ */
+ int getMetaEventType() const noexcept;
+
+ /** Returns a pointer to the data in a meta-event.
+ @see isMetaEvent, getMetaEventLength
+ */
+ const uint8* getMetaEventData() const noexcept;
+
+ /** Returns the length of the data for a meta-event.
+ @see isMetaEvent, getMetaEventData
+ */
+ int getMetaEventLength() const noexcept;
+
+ //==============================================================================
+ /** Returns true if this is a 'track' meta-event. */
+ bool isTrackMetaEvent() const noexcept;
+
+ /** Returns true if this is an 'end-of-track' meta-event. */
+ bool isEndOfTrackMetaEvent() const noexcept;
+
+ /** Creates an end-of-track meta-event.
+ @see isEndOfTrackMetaEvent
+ */
+ static MidiMessage endOfTrack() noexcept;
+
+ /** Returns true if this is an 'track name' meta-event.
+ You can use the getTextFromTextMetaEvent() method to get the track's name.
+ */
+ bool isTrackNameEvent() const noexcept;
+
+ /** Returns true if this is a 'text' meta-event.
+ @see getTextFromTextMetaEvent
+ */
+ bool isTextMetaEvent() const noexcept;
+
+ /** Returns the text from a text meta-event.
+ @see isTextMetaEvent
+ */
+ String getTextFromTextMetaEvent() const;
+
+ /** Creates a text meta-event. */
+ static MidiMessage textMetaEvent (int type, StringRef text);
+
+ //==============================================================================
+ /** Returns true if this is a 'tempo' meta-event.
+ @see getTempoMetaEventTickLength, getTempoSecondsPerQuarterNote
+ */
+ bool isTempoMetaEvent() const noexcept;
+
+ /** Returns the tick length from a tempo meta-event.
+
+ @param timeFormat the 16-bit time format value from the midi file's header.
+ @returns the tick length (in seconds).
+ @see isTempoMetaEvent
+ */
+ double getTempoMetaEventTickLength (short timeFormat) const noexcept;
+
+ /** Calculates the seconds-per-quarter-note from a tempo meta-event.
+ @see isTempoMetaEvent, getTempoMetaEventTickLength
+ */
+ double getTempoSecondsPerQuarterNote() const noexcept;
+
+ /** Creates a tempo meta-event.
+ @see isTempoMetaEvent
+ */
+ static MidiMessage tempoMetaEvent (int microsecondsPerQuarterNote) noexcept;
+
+ //==============================================================================
+ /** Returns true if this is a 'time-signature' meta-event.
+ @see getTimeSignatureInfo
+ */
+ bool isTimeSignatureMetaEvent() const noexcept;
+
+ /** Returns the time-signature values from a time-signature meta-event.
+ @see isTimeSignatureMetaEvent
+ */
+ void getTimeSignatureInfo (int& numerator, int& denominator) const noexcept;
+
+ /** Creates a time-signature meta-event.
+ @see isTimeSignatureMetaEvent
+ */
+ static MidiMessage timeSignatureMetaEvent (int numerator, int denominator);
+
+ //==============================================================================
+ /** Returns true if this is a 'key-signature' meta-event.
+ @see getKeySignatureNumberOfSharpsOrFlats, isKeySignatureMajorKey
+ */
+ bool isKeySignatureMetaEvent() const noexcept;
+
+ /** Returns the key from a key-signature meta-event.
+ This method must only be called if isKeySignatureMetaEvent() is true.
+ A positive number here indicates the number of sharps in the key signature,
+ and a negative number indicates a number of flats. So e.g. 3 = F# + C# + G#,
+ -2 = Bb + Eb
+ @see isKeySignatureMetaEvent, isKeySignatureMajorKey
+ */
+ int getKeySignatureNumberOfSharpsOrFlats() const noexcept;
+
+ /** Returns true if this key-signature event is major, or false if it's minor.
+ This method must only be called if isKeySignatureMetaEvent() is true.
+ */
+ bool isKeySignatureMajorKey() const noexcept;
+
+ /** Creates a key-signature meta-event.
+ @param numberOfSharpsOrFlats if positive, this indicates the number of sharps
+ in the key; if negative, the number of flats
+ @param isMinorKey if true, the key is minor; if false, it is major
+ @see isKeySignatureMetaEvent
+ */
+ static MidiMessage keySignatureMetaEvent (int numberOfSharpsOrFlats, bool isMinorKey);
+
+ //==============================================================================
+ /** Returns true if this is a 'channel' meta-event.
+
+ A channel meta-event specifies the midi channel that should be used
+ for subsequent meta-events.
+
+ @see getMidiChannelMetaEventChannel
+ */
+ bool isMidiChannelMetaEvent() const noexcept;
+
+ /** Returns the channel number from a channel meta-event.
+
+ @returns the channel, in the range 1 to 16.
+ @see isMidiChannelMetaEvent
+ */
+ int getMidiChannelMetaEventChannel() const noexcept;
+
+ /** Creates a midi channel meta-event.
+
+ @param channel the midi channel, in the range 1 to 16
+ @see isMidiChannelMetaEvent
+ */
+ static MidiMessage midiChannelMetaEvent (int channel) noexcept;
+
+ //==============================================================================
+ /** Returns true if this is an active-sense message. */
+ bool isActiveSense() const noexcept;
+
+ //==============================================================================
+ /** Returns true if this is a midi start event.
+ @see midiStart
+ */
+ bool isMidiStart() const noexcept;
+
+ /** Creates a midi start event. */
+ static MidiMessage midiStart() noexcept;
+
+ /** Returns true if this is a midi continue event.
+ @see midiContinue
+ */
+ bool isMidiContinue() const noexcept;
+
+ /** Creates a midi continue event. */
+ static MidiMessage midiContinue() noexcept;
+
+ /** Returns true if this is a midi stop event.
+ @see midiStop
+ */
+ bool isMidiStop() const noexcept;
+
+ /** Creates a midi stop event. */
+ static MidiMessage midiStop() noexcept;
+
+ /** Returns true if this is a midi clock event.
+ @see midiClock, songPositionPointer
+ */
+ bool isMidiClock() const noexcept;
+
+ /** Creates a midi clock event. */
+ static MidiMessage midiClock() noexcept;
+
+ /** Returns true if this is a song-position-pointer message.
+ @see getSongPositionPointerMidiBeat, songPositionPointer
+ */
+ bool isSongPositionPointer() const noexcept;
+
+ /** Returns the midi beat-number of a song-position-pointer message.
+ @see isSongPositionPointer, songPositionPointer
+ */
+ int getSongPositionPointerMidiBeat() const noexcept;
+
+ /** Creates a song-position-pointer message.
+
+ The position is a number of midi beats from the start of the song, where 1 midi
+ beat is 6 midi clocks, and there are 24 midi clocks in a quarter-note. So there
+ are 4 midi beats in a quarter-note.
+
+ @see isSongPositionPointer, getSongPositionPointerMidiBeat
+ */
+ static MidiMessage songPositionPointer (int positionInMidiBeats) noexcept;
+
+ //==============================================================================
+ /** Returns true if this is a quarter-frame midi timecode message.
+ @see quarterFrame, getQuarterFrameSequenceNumber, getQuarterFrameValue
+ */
+ bool isQuarterFrame() const noexcept;
+
+ /** Returns the sequence number of a quarter-frame midi timecode message.
+ This will be a value between 0 and 7.
+ @see isQuarterFrame, getQuarterFrameValue, quarterFrame
+ */
+ int getQuarterFrameSequenceNumber() const noexcept;
+
+ /** Returns the value from a quarter-frame message.
+ This will be the lower nybble of the message's data-byte, a value between 0 and 15
+ */
+ int getQuarterFrameValue() const noexcept;
+
+ /** Creates a quarter-frame MTC message.
+
+ @param sequenceNumber a value 0 to 7 for the upper nybble of the message's data byte
+ @param value a value 0 to 15 for the lower nybble of the message's data byte
+ */
+ static MidiMessage quarterFrame (int sequenceNumber, int value) noexcept;
+
+ /** SMPTE timecode types.
+ Used by the getFullFrameParameters() and fullFrame() methods.
+ */
+ enum SmpteTimecodeType
+ {
+ fps24 = 0,
+ fps25 = 1,
+ fps30drop = 2,
+ fps30 = 3
+ };
+
+ /** Returns true if this is a full-frame midi timecode message. */
+ bool isFullFrame() const noexcept;
+
+ /** Extracts the timecode information from a full-frame midi timecode message.
+
+ You should only call this on messages where you've used isFullFrame() to
+ check that they're the right kind.
+ */
+ void getFullFrameParameters (int& hours,
+ int& minutes,
+ int& seconds,
+ int& frames,
+ SmpteTimecodeType& timecodeType) const noexcept;
+
+ /** Creates a full-frame MTC message. */
+ static MidiMessage fullFrame (int hours,
+ int minutes,
+ int seconds,
+ int frames,
+ SmpteTimecodeType timecodeType);
+
+ //==============================================================================
+ /** Types of MMC command.
+
+ @see isMidiMachineControlMessage, getMidiMachineControlCommand, midiMachineControlCommand
+ */
+ enum MidiMachineControlCommand
+ {
+ mmc_stop = 1,
+ mmc_play = 2,
+ mmc_deferredplay = 3,
+ mmc_fastforward = 4,
+ mmc_rewind = 5,
+ mmc_recordStart = 6,
+ mmc_recordStop = 7,
+ mmc_pause = 9
+ };
+
+ /** Checks whether this is an MMC message.
+ If it is, you can use the getMidiMachineControlCommand() to find out its type.
+ */
+ bool isMidiMachineControlMessage() const noexcept;
+
+ /** For an MMC message, this returns its type.
+
+ Make sure it's actually an MMC message with isMidiMachineControlMessage() before
+ calling this method.
+ */
+ MidiMachineControlCommand getMidiMachineControlCommand() const noexcept;
+
+ /** Creates an MMC message. */
+ static MidiMessage midiMachineControlCommand (MidiMachineControlCommand command);
+
+ /** Checks whether this is an MMC "goto" message.
+ If it is, the parameters passed-in are set to the time that the message contains.
+ @see midiMachineControlGoto
+ */
+ bool isMidiMachineControlGoto (int& hours,
+ int& minutes,
+ int& seconds,
+ int& frames) const noexcept;
+
+ /** Creates an MMC "goto" message.
+ This messages tells the device to go to a specific frame.
+ @see isMidiMachineControlGoto
+ */
+ static MidiMessage midiMachineControlGoto (int hours,
+ int minutes,
+ int seconds,
+ int frames);
+
+ //==============================================================================
+ /** Creates a master-volume change message.
+ @param volume the volume, 0 to 1.0
+ */
+ static MidiMessage masterVolume (float volume);
+
+ //==============================================================================
+ /** Creates a system-exclusive message.
+ The data passed in is wrapped with header and tail bytes of 0xf0 and 0xf7.
+ */
+ static MidiMessage createSysExMessage (const void* sysexData,
+ int dataSize);
+
+
+ //==============================================================================
+ /** Reads a midi variable-length integer.
+
+ @param data the data to read the number from
+ @param numBytesUsed on return, this will be set to the number of bytes that were read
+ */
+ static int readVariableLengthVal (const uint8* data,
+ int& numBytesUsed) noexcept;
+
+ /** Based on the first byte of a short midi message, this uses a lookup table
+ to return the message length (either 1, 2, or 3 bytes).
+
+ The value passed in must be 0x80 or higher.
+ */
+ static int getMessageLengthFromFirstByte (const uint8 firstByte) noexcept;
+
+ //==============================================================================
+ /** Returns the name of a midi note number.
+
+ E.g "C", "D#", etc.
+
+ @param noteNumber the midi note number, 0 to 127
+ @param useSharps if true, sharpened notes are used, e.g. "C#", otherwise
+ they'll be flattened, e.g. "Db"
+ @param includeOctaveNumber if true, the octave number will be appended to the string,
+ e.g. "C#4"
+ @param octaveNumForMiddleC if an octave number is being appended, this indicates the
+ number that will be used for middle C's octave
+
+ @see getMidiNoteInHertz
+ */
+ static String getMidiNoteName (int noteNumber,
+ bool useSharps,
+ bool includeOctaveNumber,
+ int octaveNumForMiddleC);
+
+ /** Returns the frequency of a midi note number.
+
+ The frequencyOfA parameter is an optional frequency for 'A', normally 440-444Hz for concert pitch.
+ @see getMidiNoteName
+ */
+ static double getMidiNoteInHertz (int noteNumber, const double frequencyOfA = 440.0) noexcept;
+
+ /** Returns true if the given midi note number is a black key. */
+ static bool isMidiNoteBlack (int noteNumber) noexcept;
+
+ /** Returns the standard name of a GM instrument, or nullptr if unknown for this index.
+
+ @param midiInstrumentNumber the program number 0 to 127
+ @see getProgramChangeNumber
+ */
+ static const char* getGMInstrumentName (int midiInstrumentNumber);
+
+ /** Returns the name of a bank of GM instruments, or nullptr if unknown for this bank number.
+ @param midiBankNumber the bank, 0 to 15
+ */
+ static const char* getGMInstrumentBankName (int midiBankNumber);
+
+ /** Returns the standard name of a channel 10 percussion sound, or nullptr if unknown for this note number.
+ @param midiNoteNumber the key number, 35 to 81
+ */
+ static const char* getRhythmInstrumentName (int midiNoteNumber);
+
+ /** Returns the name of a controller type number, or nullptr if unknown for this controller number.
+ @see getControllerNumber
+ */
+ static const char* getControllerName (int controllerNumber);
+
+private:
+ //==============================================================================
+ double timeStamp;
+ HeapBlock allocatedData;
+ int size;
+
+ #ifndef DOXYGEN
+ union
+ {
+ uint8 asBytes[4];
+ uint32 asInt32;
+ } preallocatedData;
+ #endif
+
+ inline uint8* getData() noexcept { return allocatedData != nullptr ? allocatedData.getData() : preallocatedData.asBytes; }
+ uint8* allocateSpace (int);
+};
+
+#endif // JUCE_MIDIMESSAGE_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp
new file mode 100644
index 0000000..94a6b9b
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp
@@ -0,0 +1,331 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+MidiMessageSequence::MidiMessageSequence()
+{
+}
+
+MidiMessageSequence::MidiMessageSequence (const MidiMessageSequence& other)
+{
+ list.addCopiesOf (other.list);
+ updateMatchedPairs();
+}
+
+MidiMessageSequence& MidiMessageSequence::operator= (const MidiMessageSequence& other)
+{
+ MidiMessageSequence otherCopy (other);
+ swapWith (otherCopy);
+ return *this;
+}
+
+void MidiMessageSequence::swapWith (MidiMessageSequence& other) noexcept
+{
+ list.swapWith (other.list);
+}
+
+MidiMessageSequence::~MidiMessageSequence()
+{
+}
+
+void MidiMessageSequence::clear()
+{
+ list.clear();
+}
+
+int MidiMessageSequence::getNumEvents() const noexcept
+{
+ return list.size();
+}
+
+MidiMessageSequence::MidiEventHolder* MidiMessageSequence::getEventPointer (const int index) const noexcept
+{
+ return list [index];
+}
+
+double MidiMessageSequence::getTimeOfMatchingKeyUp (const int index) const noexcept
+{
+ if (const MidiEventHolder* const meh = list [index])
+ if (meh->noteOffObject != nullptr)
+ return meh->noteOffObject->message.getTimeStamp();
+
+ return 0.0;
+}
+
+int MidiMessageSequence::getIndexOfMatchingKeyUp (const int index) const noexcept
+{
+ if (const MidiEventHolder* const meh = list [index])
+ return list.indexOf (meh->noteOffObject);
+
+ return -1;
+}
+
+int MidiMessageSequence::getIndexOf (MidiEventHolder* const event) const noexcept
+{
+ return list.indexOf (event);
+}
+
+int MidiMessageSequence::getNextIndexAtTime (const double timeStamp) const noexcept
+{
+ const int numEvents = list.size();
+
+ int i;
+ for (i = 0; i < numEvents; ++i)
+ if (list.getUnchecked(i)->message.getTimeStamp() >= timeStamp)
+ break;
+
+ return i;
+}
+
+//==============================================================================
+double MidiMessageSequence::getStartTime() const noexcept
+{
+ return getEventTime (0);
+}
+
+double MidiMessageSequence::getEndTime() const noexcept
+{
+ return getEventTime (list.size() - 1);
+}
+
+double MidiMessageSequence::getEventTime (const int index) const noexcept
+{
+ if (const MidiEventHolder* const meh = list [index])
+ return meh->message.getTimeStamp();
+
+ return 0.0;
+}
+
+//==============================================================================
+MidiMessageSequence::MidiEventHolder* MidiMessageSequence::addEvent (const MidiMessage& newMessage,
+ double timeAdjustment)
+{
+ MidiEventHolder* const newOne = new MidiEventHolder (newMessage);
+
+ timeAdjustment += newMessage.getTimeStamp();
+ newOne->message.setTimeStamp (timeAdjustment);
+
+ int i;
+ for (i = list.size(); --i >= 0;)
+ if (list.getUnchecked(i)->message.getTimeStamp() <= timeAdjustment)
+ break;
+
+ list.insert (i + 1, newOne);
+ return newOne;
+}
+
+void MidiMessageSequence::deleteEvent (const int index,
+ const bool deleteMatchingNoteUp)
+{
+ if (isPositiveAndBelow (index, list.size()))
+ {
+ if (deleteMatchingNoteUp)
+ deleteEvent (getIndexOfMatchingKeyUp (index), false);
+
+ list.remove (index);
+ }
+}
+
+struct MidiMessageSequenceSorter
+{
+ static int compareElements (const MidiMessageSequence::MidiEventHolder* const first,
+ const MidiMessageSequence::MidiEventHolder* const second) noexcept
+ {
+ const double diff = first->message.getTimeStamp() - second->message.getTimeStamp();
+ return (diff > 0) - (diff < 0);
+ }
+};
+
+void MidiMessageSequence::addSequence (const MidiMessageSequence& other,
+ double timeAdjustment,
+ double firstAllowableTime,
+ double endOfAllowableDestTimes)
+{
+ firstAllowableTime -= timeAdjustment;
+ endOfAllowableDestTimes -= timeAdjustment;
+
+ for (int i = 0; i < other.list.size(); ++i)
+ {
+ const MidiMessage& m = other.list.getUnchecked(i)->message;
+ const double t = m.getTimeStamp();
+
+ if (t >= firstAllowableTime && t < endOfAllowableDestTimes)
+ {
+ MidiEventHolder* const newOne = new MidiEventHolder (m);
+ newOne->message.setTimeStamp (timeAdjustment + t);
+
+ list.add (newOne);
+ }
+ }
+
+ sort();
+}
+
+//==============================================================================
+void MidiMessageSequence::sort() noexcept
+{
+ MidiMessageSequenceSorter sorter;
+ list.sort (sorter, true);
+}
+
+void MidiMessageSequence::updateMatchedPairs() noexcept
+{
+ for (int i = 0; i < list.size(); ++i)
+ {
+ MidiEventHolder* const meh = list.getUnchecked(i);
+ const MidiMessage& m1 = meh->message;
+
+ if (m1.isNoteOn())
+ {
+ meh->noteOffObject = nullptr;
+ const int note = m1.getNoteNumber();
+ const int chan = m1.getChannel();
+ const int len = list.size();
+
+ for (int j = i + 1; j < len; ++j)
+ {
+ const MidiMessage& m = list.getUnchecked(j)->message;
+
+ if (m.getNoteNumber() == note && m.getChannel() == chan)
+ {
+ if (m.isNoteOff())
+ {
+ meh->noteOffObject = list[j];
+ break;
+ }
+ else if (m.isNoteOn())
+ {
+ MidiEventHolder* const newEvent = new MidiEventHolder (MidiMessage::noteOff (chan, note));
+ list.insert (j, newEvent);
+ newEvent->message.setTimeStamp (m.getTimeStamp());
+ meh->noteOffObject = newEvent;
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+void MidiMessageSequence::addTimeToMessages (const double delta) noexcept
+{
+ for (int i = list.size(); --i >= 0;)
+ {
+ MidiMessage& mm = list.getUnchecked(i)->message;
+ mm.setTimeStamp (mm.getTimeStamp() + delta);
+ }
+}
+
+//==============================================================================
+void MidiMessageSequence::extractMidiChannelMessages (const int channelNumberToExtract,
+ MidiMessageSequence& destSequence,
+ const bool alsoIncludeMetaEvents) const
+{
+ for (int i = 0; i < list.size(); ++i)
+ {
+ const MidiMessage& mm = list.getUnchecked(i)->message;
+
+ if (mm.isForChannel (channelNumberToExtract) || (alsoIncludeMetaEvents && mm.isMetaEvent()))
+ destSequence.addEvent (mm);
+ }
+}
+
+void MidiMessageSequence::extractSysExMessages (MidiMessageSequence& destSequence) const
+{
+ for (int i = 0; i < list.size(); ++i)
+ {
+ const MidiMessage& mm = list.getUnchecked(i)->message;
+
+ if (mm.isSysEx())
+ destSequence.addEvent (mm);
+ }
+}
+
+void MidiMessageSequence::deleteMidiChannelMessages (const int channelNumberToRemove)
+{
+ for (int i = list.size(); --i >= 0;)
+ if (list.getUnchecked(i)->message.isForChannel (channelNumberToRemove))
+ list.remove(i);
+}
+
+void MidiMessageSequence::deleteSysExMessages()
+{
+ for (int i = list.size(); --i >= 0;)
+ if (list.getUnchecked(i)->message.isSysEx())
+ list.remove(i);
+}
+
+//==============================================================================
+void MidiMessageSequence::createControllerUpdatesForTime (const int channelNumber,
+ const double time,
+ OwnedArray& dest)
+{
+ bool doneProg = false;
+ bool donePitchWheel = false;
+ Array doneControllers;
+ doneControllers.ensureStorageAllocated (32);
+
+ for (int i = list.size(); --i >= 0;)
+ {
+ const MidiMessage& mm = list.getUnchecked(i)->message;
+
+ if (mm.isForChannel (channelNumber) && mm.getTimeStamp() <= time)
+ {
+ if (mm.isProgramChange())
+ {
+ if (! doneProg)
+ {
+ dest.add (new MidiMessage (mm, 0.0));
+ doneProg = true;
+ }
+ }
+ else if (mm.isController())
+ {
+ if (! doneControllers.contains (mm.getControllerNumber()))
+ {
+ dest.add (new MidiMessage (mm, 0.0));
+ doneControllers.add (mm.getControllerNumber());
+ }
+ }
+ else if (mm.isPitchWheel())
+ {
+ if (! donePitchWheel)
+ {
+ dest.add (new MidiMessage (mm, 0.0));
+ donePitchWheel = true;
+ }
+ }
+ }
+ }
+}
+
+
+//==============================================================================
+MidiMessageSequence::MidiEventHolder::MidiEventHolder (const MidiMessage& mm)
+ : message (mm), noteOffObject (nullptr)
+{
+}
+
+MidiMessageSequence::MidiEventHolder::~MidiEventHolder()
+{
+}
diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h
new file mode 100644
index 0000000..b62940c
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h
@@ -0,0 +1,265 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_MIDIMESSAGESEQUENCE_H_INCLUDED
+#define JUCE_MIDIMESSAGESEQUENCE_H_INCLUDED
+
+
+//==============================================================================
+/**
+ A sequence of timestamped midi messages.
+
+ This allows the sequence to be manipulated, and also to be read from and
+ written to a standard midi file.
+
+ @see MidiMessage, MidiFile
+*/
+class JUCE_API MidiMessageSequence
+{
+public:
+ //==============================================================================
+ /** Creates an empty midi sequence object. */
+ MidiMessageSequence();
+
+ /** Creates a copy of another sequence. */
+ MidiMessageSequence (const MidiMessageSequence&);
+
+ /** Replaces this sequence with another one. */
+ MidiMessageSequence& operator= (const MidiMessageSequence&);
+
+ /** Destructor. */
+ ~MidiMessageSequence();
+
+ //==============================================================================
+ /** Structure used to hold midi events in the sequence.
+
+ These structures act as 'handles' on the events as they are moved about in
+ the list, and make it quick to find the matching note-offs for note-on events.
+
+ @see MidiMessageSequence::getEventPointer
+ */
+ class MidiEventHolder
+ {
+ public:
+ //==============================================================================
+ /** Destructor. */
+ ~MidiEventHolder();
+
+ /** The message itself, whose timestamp is used to specify the event's time. */
+ MidiMessage message;
+
+ /** The matching note-off event (if this is a note-on event).
+
+ If this isn't a note-on, this pointer will be nullptr.
+
+ Use the MidiMessageSequence::updateMatchedPairs() method to keep these
+ note-offs up-to-date after events have been moved around in the sequence
+ or deleted.
+ */
+ MidiEventHolder* noteOffObject;
+
+ private:
+ //==============================================================================
+ friend class MidiMessageSequence;
+ MidiEventHolder (const MidiMessage&);
+ JUCE_LEAK_DETECTOR (MidiEventHolder)
+ };
+
+ //==============================================================================
+ /** Clears the sequence. */
+ void clear();
+
+ /** Returns the number of events in the sequence. */
+ int getNumEvents() const noexcept;
+
+ /** Returns a pointer to one of the events. */
+ MidiEventHolder* getEventPointer (int index) const noexcept;
+
+ /** Returns the time of the note-up that matches the note-on at this index.
+ If the event at this index isn't a note-on, it'll just return 0.
+ @see MidiMessageSequence::MidiEventHolder::noteOffObject
+ */
+ double getTimeOfMatchingKeyUp (int index) const noexcept;
+
+ /** Returns the index of the note-up that matches the note-on at this index.
+ If the event at this index isn't a note-on, it'll just return -1.
+ @see MidiMessageSequence::MidiEventHolder::noteOffObject
+ */
+ int getIndexOfMatchingKeyUp (int index) const noexcept;
+
+ /** Returns the index of an event. */
+ int getIndexOf (MidiEventHolder* event) const noexcept;
+
+ /** Returns the index of the first event on or after the given timestamp.
+ If the time is beyond the end of the sequence, this will return the
+ number of events.
+ */
+ int getNextIndexAtTime (double timeStamp) const noexcept;
+
+ //==============================================================================
+ /** Returns the timestamp of the first event in the sequence.
+ @see getEndTime
+ */
+ double getStartTime() const noexcept;
+
+ /** Returns the timestamp of the last event in the sequence.
+ @see getStartTime
+ */
+ double getEndTime() const noexcept;
+
+ /** Returns the timestamp of the event at a given index.
+ If the index is out-of-range, this will return 0.0
+ */
+ double getEventTime (int index) const noexcept;
+
+ //==============================================================================
+ /** Inserts a midi message into the sequence.
+
+ The index at which the new message gets inserted will depend on its timestamp,
+ because the sequence is kept sorted.
+
+ Remember to call updateMatchedPairs() after adding note-on events.
+
+ @param newMessage the new message to add (an internal copy will be made)
+ @param timeAdjustment an optional value to add to the timestamp of the message
+ that will be inserted
+ @see updateMatchedPairs
+ */
+ MidiEventHolder* addEvent (const MidiMessage& newMessage,
+ double timeAdjustment = 0);
+
+ /** Deletes one of the events in the sequence.
+
+ Remember to call updateMatchedPairs() after removing events.
+
+ @param index the index of the event to delete
+ @param deleteMatchingNoteUp whether to also remove the matching note-off
+ if the event you're removing is a note-on
+ */
+ void deleteEvent (int index, bool deleteMatchingNoteUp);
+
+ /** Merges another sequence into this one.
+
+ Remember to call updateMatchedPairs() after using this method.
+
+ @param other the sequence to add from
+ @param timeAdjustmentDelta an amount to add to the timestamps of the midi events
+ as they are read from the other sequence
+ @param firstAllowableDestTime events will not be added if their time is earlier
+ than this time. (This is after their time has been adjusted
+ by the timeAdjustmentDelta)
+ @param endOfAllowableDestTimes events will not be added if their time is equal to
+ or greater than this time. (This is after their time has
+ been adjusted by the timeAdjustmentDelta)
+ */
+ void addSequence (const MidiMessageSequence& other,
+ double timeAdjustmentDelta,
+ double firstAllowableDestTime,
+ double endOfAllowableDestTimes);
+
+ //==============================================================================
+ /** Makes sure all the note-on and note-off pairs are up-to-date.
+
+ Call this after re-ordering messages or deleting/adding messages, and it
+ will scan the list and make sure all the note-offs in the MidiEventHolder
+ structures are pointing at the correct ones.
+ */
+ void updateMatchedPairs() noexcept;
+
+ /** Forces a sort of the sequence.
+ You may need to call this if you've manually modified the timestamps of some
+ events such that the overall order now needs updating.
+ */
+ void sort() noexcept;
+
+ //==============================================================================
+ /** Copies all the messages for a particular midi channel to another sequence.
+
+ @param channelNumberToExtract the midi channel to look for, in the range 1 to 16
+ @param destSequence the sequence that the chosen events should be copied to
+ @param alsoIncludeMetaEvents if true, any meta-events (which don't apply to a specific
+ channel) will also be copied across.
+ @see extractSysExMessages
+ */
+ void extractMidiChannelMessages (int channelNumberToExtract,
+ MidiMessageSequence& destSequence,
+ bool alsoIncludeMetaEvents) const;
+
+ /** Copies all midi sys-ex messages to another sequence.
+ @param destSequence this is the sequence to which any sys-exes in this sequence
+ will be added
+ @see extractMidiChannelMessages
+ */
+ void extractSysExMessages (MidiMessageSequence& destSequence) const;
+
+ /** Removes any messages in this sequence that have a specific midi channel.
+ @param channelNumberToRemove the midi channel to look for, in the range 1 to 16
+ */
+ void deleteMidiChannelMessages (int channelNumberToRemove);
+
+ /** Removes any sys-ex messages from this sequence. */
+ void deleteSysExMessages();
+
+ /** Adds an offset to the timestamps of all events in the sequence.
+ @param deltaTime the amount to add to each timestamp.
+ */
+ void addTimeToMessages (double deltaTime) noexcept;
+
+ //==============================================================================
+ /** Scans through the sequence to determine the state of any midi controllers at
+ a given time.
+
+ This will create a sequence of midi controller changes that can be
+ used to set all midi controllers to the state they would be in at the
+ specified time within this sequence.
+
+ As well as controllers, it will also recreate the midi program number
+ and pitch bend position.
+
+ @param channelNumber the midi channel to look for, in the range 1 to 16. Controllers
+ for other channels will be ignored.
+ @param time the time at which you want to find out the state - there are
+ no explicit units for this time measurement, it's the same units
+ as used for the timestamps of the messages
+ @param resultMessages an array to which midi controller-change messages will be added. This
+ will be the minimum number of controller changes to recreate the
+ state at the required time.
+ */
+ void createControllerUpdatesForTime (int channelNumber, double time,
+ OwnedArray& resultMessages);
+
+ //==============================================================================
+ /** Swaps this sequence with another one. */
+ void swapWith (MidiMessageSequence&) noexcept;
+
+private:
+ //==============================================================================
+ friend class MidiFile;
+ OwnedArray list;
+
+ JUCE_LEAK_DETECTOR (MidiMessageSequence)
+};
+
+
+#endif // JUCE_MIDIMESSAGESEQUENCE_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_AudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_AudioSource.h
new file mode 100644
index 0000000..cfbd59f
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_AudioSource.h
@@ -0,0 +1,181 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_AUDIOSOURCE_H_INCLUDED
+#define JUCE_AUDIOSOURCE_H_INCLUDED
+
+
+//==============================================================================
+/**
+ Used by AudioSource::getNextAudioBlock().
+*/
+struct JUCE_API AudioSourceChannelInfo
+{
+ /** Creates an uninitialised AudioSourceChannelInfo. */
+ AudioSourceChannelInfo() noexcept
+ {
+ }
+
+ /** Creates an AudioSourceChannelInfo. */
+ AudioSourceChannelInfo (AudioSampleBuffer* bufferToUse,
+ int startSampleOffset, int numSamplesToUse) noexcept
+ : buffer (bufferToUse),
+ startSample (startSampleOffset),
+ numSamples (numSamplesToUse)
+ {
+ }
+
+ /** Creates an AudioSourceChannelInfo that uses the whole of a buffer.
+ Note that the buffer provided must not be deleted while the
+ AudioSourceChannelInfo is still using it.
+ */
+ explicit AudioSourceChannelInfo (AudioSampleBuffer& bufferToUse) noexcept
+ : buffer (&bufferToUse),
+ startSample (0),
+ numSamples (bufferToUse.getNumSamples())
+ {
+ }
+
+ /** The destination buffer to fill with audio data.
+
+ When the AudioSource::getNextAudioBlock() method is called, the active section
+ of this buffer should be filled with whatever output the source produces.
+
+ Only the samples specified by the startSample and numSamples members of this structure
+ should be affected by the call.
+
+ The contents of the buffer when it is passed to the AudioSource::getNextAudioBlock()
+ method can be treated as the input if the source is performing some kind of filter operation,
+ but should be cleared if this is not the case - the clearActiveBufferRegion() is
+ a handy way of doing this.
+
+ The number of channels in the buffer could be anything, so the AudioSource
+ must cope with this in whatever way is appropriate for its function.
+ */
+ AudioSampleBuffer* buffer;
+
+ /** The first sample in the buffer from which the callback is expected
+ to write data. */
+ int startSample;
+
+ /** The number of samples in the buffer which the callback is expected to
+ fill with data. */
+ int numSamples;
+
+ /** Convenient method to clear the buffer if the source is not producing any data. */
+ void clearActiveBufferRegion() const
+ {
+ if (buffer != nullptr)
+ buffer->clear (startSample, numSamples);
+ }
+};
+
+
+//==============================================================================
+/**
+ Base class for objects that can produce a continuous stream of audio.
+
+ An AudioSource has two states: 'prepared' and 'unprepared'.
+
+ When a source needs to be played, it is first put into a 'prepared' state by a call to
+ prepareToPlay(), and then repeated calls will be made to its getNextAudioBlock() method to
+ process the audio data.
+
+ Once playback has finished, the releaseResources() method is called to put the stream
+ back into an 'unprepared' state.
+
+ @see AudioFormatReaderSource, ResamplingAudioSource
+*/
+class JUCE_API AudioSource
+{
+protected:
+ //==============================================================================
+ /** Creates an AudioSource. */
+ AudioSource() noexcept {}
+
+public:
+ /** Destructor. */
+ virtual ~AudioSource() {}
+
+ //==============================================================================
+ /** Tells the source to prepare for playing.
+
+ An AudioSource has two states: prepared and unprepared.
+
+ The prepareToPlay() method is guaranteed to be called at least once on an 'unpreprared'
+ source to put it into a 'prepared' state before any calls will be made to getNextAudioBlock().
+ This callback allows the source to initialise any resources it might need when playing.
+
+ Once playback has finished, the releaseResources() method is called to put the stream
+ back into an 'unprepared' state.
+
+ Note that this method could be called more than once in succession without
+ a matching call to releaseResources(), so make sure your code is robust and
+ can handle that kind of situation.
+
+ @param samplesPerBlockExpected the number of samples that the source
+ will be expected to supply each time its
+ getNextAudioBlock() method is called. This
+ number may vary slightly, because it will be dependent
+ on audio hardware callbacks, and these aren't
+ guaranteed to always use a constant block size, so
+ the source should be able to cope with small variations.
+ @param sampleRate the sample rate that the output will be used at - this
+ is needed by sources such as tone generators.
+ @see releaseResources, getNextAudioBlock
+ */
+ virtual void prepareToPlay (int samplesPerBlockExpected,
+ double sampleRate) = 0;
+
+ /** Allows the source to release anything it no longer needs after playback has stopped.
+
+ This will be called when the source is no longer going to have its getNextAudioBlock()
+ method called, so it should release any spare memory, etc. that it might have
+ allocated during the prepareToPlay() call.
+
+ Note that there's no guarantee that prepareToPlay() will actually have been called before
+ releaseResources(), and it may be called more than once in succession, so make sure your
+ code is robust and doesn't make any assumptions about when it will be called.
+
+ @see prepareToPlay, getNextAudioBlock
+ */
+ virtual void releaseResources() = 0;
+
+ /** Called repeatedly to fetch subsequent blocks of audio data.
+
+ After calling the prepareToPlay() method, this callback will be made each
+ time the audio playback hardware (or whatever other destination the audio
+ data is going to) needs another block of data.
+
+ It will generally be called on a high-priority system thread, or possibly even
+ an interrupt, so be careful not to do too much work here, as that will cause
+ audio glitches!
+
+ @see AudioSourceChannelInfo, prepareToPlay, releaseResources
+ */
+ virtual void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) = 0;
+};
+
+
+#endif // JUCE_AUDIOSOURCE_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp
new file mode 100644
index 0000000..21dc173
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp
@@ -0,0 +1,259 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* s,
+ TimeSliceThread& thread,
+ const bool deleteSourceWhenDeleted,
+ const int bufferSizeSamples,
+ const int numChannels)
+ : source (s, deleteSourceWhenDeleted),
+ backgroundThread (thread),
+ numberOfSamplesToBuffer (jmax (1024, bufferSizeSamples)),
+ numberOfChannels (numChannels),
+ bufferValidStart (0),
+ bufferValidEnd (0),
+ nextPlayPos (0),
+ sampleRate (0),
+ wasSourceLooping (false),
+ isPrepared (false)
+{
+ jassert (source != nullptr);
+
+ jassert (numberOfSamplesToBuffer > 1024); // not much point using this class if you're
+ // not using a larger buffer..
+}
+
+BufferingAudioSource::~BufferingAudioSource()
+{
+ releaseResources();
+}
+
+//==============================================================================
+void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double newSampleRate)
+{
+ const int bufferSizeNeeded = jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer);
+
+ if (newSampleRate != sampleRate
+ || bufferSizeNeeded != buffer.getNumSamples()
+ || ! isPrepared)
+ {
+ backgroundThread.removeTimeSliceClient (this);
+
+ isPrepared = true;
+ sampleRate = newSampleRate;
+
+ source->prepareToPlay (samplesPerBlockExpected, newSampleRate);
+
+ buffer.setSize (numberOfChannels, bufferSizeNeeded);
+ buffer.clear();
+
+ bufferValidStart = 0;
+ bufferValidEnd = 0;
+
+ backgroundThread.addTimeSliceClient (this);
+
+ while (bufferValidEnd - bufferValidStart < jmin (((int) newSampleRate) / 4,
+ buffer.getNumSamples() / 2))
+ {
+ backgroundThread.moveToFrontOfQueue (this);
+ Thread::sleep (5);
+ }
+ }
+}
+
+void BufferingAudioSource::releaseResources()
+{
+ isPrepared = false;
+ backgroundThread.removeTimeSliceClient (this);
+
+ buffer.setSize (numberOfChannels, 0);
+ source->releaseResources();
+}
+
+void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
+{
+ const ScopedLock sl (bufferStartPosLock);
+
+ const int validStart = (int) (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos) - nextPlayPos);
+ const int validEnd = (int) (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos + info.numSamples) - nextPlayPos);
+
+ if (validStart == validEnd)
+ {
+ // total cache miss
+ info.clearActiveBufferRegion();
+ }
+ else
+ {
+ if (validStart > 0)
+ info.buffer->clear (info.startSample, validStart); // partial cache miss at start
+
+ if (validEnd < info.numSamples)
+ info.buffer->clear (info.startSample + validEnd,
+ info.numSamples - validEnd); // partial cache miss at end
+
+ if (validStart < validEnd)
+ {
+ for (int chan = jmin (numberOfChannels, info.buffer->getNumChannels()); --chan >= 0;)
+ {
+ jassert (buffer.getNumSamples() > 0);
+ const int startBufferIndex = (int) ((validStart + nextPlayPos) % buffer.getNumSamples());
+ const int endBufferIndex = (int) ((validEnd + nextPlayPos) % buffer.getNumSamples());
+
+ if (startBufferIndex < endBufferIndex)
+ {
+ info.buffer->copyFrom (chan, info.startSample + validStart,
+ buffer,
+ chan, startBufferIndex,
+ validEnd - validStart);
+ }
+ else
+ {
+ const int initialSize = buffer.getNumSamples() - startBufferIndex;
+
+ info.buffer->copyFrom (chan, info.startSample + validStart,
+ buffer,
+ chan, startBufferIndex,
+ initialSize);
+
+ info.buffer->copyFrom (chan, info.startSample + validStart + initialSize,
+ buffer,
+ chan, 0,
+ (validEnd - validStart) - initialSize);
+ }
+ }
+ }
+
+ nextPlayPos += info.numSamples;
+ }
+}
+
+int64 BufferingAudioSource::getNextReadPosition() const
+{
+ jassert (source->getTotalLength() > 0);
+ return (source->isLooping() && nextPlayPos > 0)
+ ? nextPlayPos % source->getTotalLength()
+ : nextPlayPos;
+}
+
+void BufferingAudioSource::setNextReadPosition (int64 newPosition)
+{
+ const ScopedLock sl (bufferStartPosLock);
+
+ nextPlayPos = newPosition;
+ backgroundThread.moveToFrontOfQueue (this);
+}
+
+bool BufferingAudioSource::readNextBufferChunk()
+{
+ int64 newBVS, newBVE, sectionToReadStart, sectionToReadEnd;
+
+ {
+ const ScopedLock sl (bufferStartPosLock);
+
+ if (wasSourceLooping != isLooping())
+ {
+ wasSourceLooping = isLooping();
+ bufferValidStart = 0;
+ bufferValidEnd = 0;
+ }
+
+ newBVS = jmax ((int64) 0, nextPlayPos);
+ newBVE = newBVS + buffer.getNumSamples() - 4;
+ sectionToReadStart = 0;
+ sectionToReadEnd = 0;
+
+ const int maxChunkSize = 2048;
+
+ if (newBVS < bufferValidStart || newBVS >= bufferValidEnd)
+ {
+ newBVE = jmin (newBVE, newBVS + maxChunkSize);
+
+ sectionToReadStart = newBVS;
+ sectionToReadEnd = newBVE;
+
+ bufferValidStart = 0;
+ bufferValidEnd = 0;
+ }
+ else if (std::abs ((int) (newBVS - bufferValidStart)) > 512
+ || std::abs ((int) (newBVE - bufferValidEnd)) > 512)
+ {
+ newBVE = jmin (newBVE, bufferValidEnd + maxChunkSize);
+
+ sectionToReadStart = bufferValidEnd;
+ sectionToReadEnd = newBVE;
+
+ bufferValidStart = newBVS;
+ bufferValidEnd = jmin (bufferValidEnd, newBVE);
+ }
+ }
+
+ if (sectionToReadStart == sectionToReadEnd)
+ return false;
+
+ jassert (buffer.getNumSamples() > 0);
+ const int bufferIndexStart = (int) (sectionToReadStart % buffer.getNumSamples());
+ const int bufferIndexEnd = (int) (sectionToReadEnd % buffer.getNumSamples());
+
+ if (bufferIndexStart < bufferIndexEnd)
+ {
+ readBufferSection (sectionToReadStart,
+ (int) (sectionToReadEnd - sectionToReadStart),
+ bufferIndexStart);
+ }
+ else
+ {
+ const int initialSize = buffer.getNumSamples() - bufferIndexStart;
+
+ readBufferSection (sectionToReadStart,
+ initialSize,
+ bufferIndexStart);
+
+ readBufferSection (sectionToReadStart + initialSize,
+ (int) (sectionToReadEnd - sectionToReadStart) - initialSize,
+ 0);
+ }
+
+ {
+ const ScopedLock sl2 (bufferStartPosLock);
+
+ bufferValidStart = newBVS;
+ bufferValidEnd = newBVE;
+ }
+
+ return true;
+}
+
+void BufferingAudioSource::readBufferSection (const int64 start, const int length, const int bufferOffset)
+{
+ if (source->getNextReadPosition() != start)
+ source->setNextReadPosition (start);
+
+ AudioSourceChannelInfo info (&buffer, bufferOffset, length);
+ source->getNextAudioBlock (info);
+}
+
+int BufferingAudioSource::useTimeSlice()
+{
+ return readNextBufferChunk() ? 1 : 100;
+}
diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.h
new file mode 100644
index 0000000..b0ab4d2
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.h
@@ -0,0 +1,111 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_BUFFERINGAUDIOSOURCE_H_INCLUDED
+#define JUCE_BUFFERINGAUDIOSOURCE_H_INCLUDED
+
+
+//==============================================================================
+/**
+ An AudioSource which takes another source as input, and buffers it using a thread.
+
+ Create this as a wrapper around another thread, and it will read-ahead with
+ a background thread to smooth out playback. You can either create one of these
+ directly, or use it indirectly using an AudioTransportSource.
+
+ @see PositionableAudioSource, AudioTransportSource
+*/
+class JUCE_API BufferingAudioSource : public PositionableAudioSource,
+ private TimeSliceClient
+{
+public:
+ //==============================================================================
+ /** Creates a BufferingAudioSource.
+
+ @param source the input source to read from
+ @param backgroundThread a background thread that will be used for the
+ background read-ahead. This object must not be deleted
+ until after any BufferedAudioSources that are using it
+ have been deleted!
+ @param deleteSourceWhenDeleted if true, then the input source object will
+ be deleted when this object is deleted
+ @param numberOfSamplesToBuffer the size of buffer to use for reading ahead
+ @param numberOfChannels the number of channels that will be played
+ */
+ BufferingAudioSource (PositionableAudioSource* source,
+ TimeSliceThread& backgroundThread,
+ bool deleteSourceWhenDeleted,
+ int numberOfSamplesToBuffer,
+ int numberOfChannels = 2);
+
+ /** Destructor.
+
+ The input source may be deleted depending on whether the deleteSourceWhenDeleted
+ flag was set in the constructor.
+ */
+ ~BufferingAudioSource();
+
+ //==============================================================================
+ /** Implementation of the AudioSource method. */
+ void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override;
+
+ /** Implementation of the AudioSource method. */
+ void releaseResources() override;
+
+ /** Implementation of the AudioSource method. */
+ void getNextAudioBlock (const AudioSourceChannelInfo&) override;
+
+ //==============================================================================
+ /** Implements the PositionableAudioSource method. */
+ void setNextReadPosition (int64 newPosition) override;
+
+ /** Implements the PositionableAudioSource method. */
+ int64 getNextReadPosition() const override;
+
+ /** Implements the PositionableAudioSource method. */
+ int64 getTotalLength() const override { return source->getTotalLength(); }
+
+ /** Implements the PositionableAudioSource method. */
+ bool isLooping() const override { return source->isLooping(); }
+
+private:
+ //==============================================================================
+ OptionalScopedPointer source;
+ TimeSliceThread& backgroundThread;
+ int numberOfSamplesToBuffer, numberOfChannels;
+ AudioSampleBuffer buffer;
+ CriticalSection bufferStartPosLock;
+ int64 volatile bufferValidStart, bufferValidEnd, nextPlayPos;
+ double volatile sampleRate;
+ bool wasSourceLooping, isPrepared;
+
+ bool readNextBufferChunk();
+ void readBufferSection (int64 start, int length, int bufferOffset);
+ int useTimeSlice() override;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BufferingAudioSource)
+};
+
+
+#endif // JUCE_BUFFERINGAUDIOSOURCE_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.cpp
new file mode 100644
index 0000000..7e0a58c
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.cpp
@@ -0,0 +1,184 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+ChannelRemappingAudioSource::ChannelRemappingAudioSource (AudioSource* const source_,
+ const bool deleteSourceWhenDeleted)
+ : source (source_, deleteSourceWhenDeleted),
+ requiredNumberOfChannels (2)
+{
+ remappedInfo.buffer = &buffer;
+ remappedInfo.startSample = 0;
+}
+
+ChannelRemappingAudioSource::~ChannelRemappingAudioSource() {}
+
+//==============================================================================
+void ChannelRemappingAudioSource::setNumberOfChannelsToProduce (const int requiredNumberOfChannels_)
+{
+ const ScopedLock sl (lock);
+ requiredNumberOfChannels = requiredNumberOfChannels_;
+}
+
+void ChannelRemappingAudioSource::clearAllMappings()
+{
+ const ScopedLock sl (lock);
+
+ remappedInputs.clear();
+ remappedOutputs.clear();
+}
+
+void ChannelRemappingAudioSource::setInputChannelMapping (const int destIndex, const int sourceIndex)
+{
+ const ScopedLock sl (lock);
+
+ while (remappedInputs.size() < destIndex)
+ remappedInputs.add (-1);
+
+ remappedInputs.set (destIndex, sourceIndex);
+}
+
+void ChannelRemappingAudioSource::setOutputChannelMapping (const int sourceIndex, const int destIndex)
+{
+ const ScopedLock sl (lock);
+
+ while (remappedOutputs.size() < sourceIndex)
+ remappedOutputs.add (-1);
+
+ remappedOutputs.set (sourceIndex, destIndex);
+}
+
+int ChannelRemappingAudioSource::getRemappedInputChannel (const int inputChannelIndex) const
+{
+ const ScopedLock sl (lock);
+
+ if (inputChannelIndex >= 0 && inputChannelIndex < remappedInputs.size())
+ return remappedInputs.getUnchecked (inputChannelIndex);
+
+ return -1;
+}
+
+int ChannelRemappingAudioSource::getRemappedOutputChannel (const int outputChannelIndex) const
+{
+ const ScopedLock sl (lock);
+
+ if (outputChannelIndex >= 0 && outputChannelIndex < remappedOutputs.size())
+ return remappedOutputs .getUnchecked (outputChannelIndex);
+
+ return -1;
+}
+
+//==============================================================================
+void ChannelRemappingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate)
+{
+ source->prepareToPlay (samplesPerBlockExpected, sampleRate);
+}
+
+void ChannelRemappingAudioSource::releaseResources()
+{
+ source->releaseResources();
+}
+
+void ChannelRemappingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill)
+{
+ const ScopedLock sl (lock);
+
+ buffer.setSize (requiredNumberOfChannels, bufferToFill.numSamples, false, false, true);
+
+ const int numChans = bufferToFill.buffer->getNumChannels();
+
+ for (int i = 0; i < buffer.getNumChannels(); ++i)
+ {
+ const int remappedChan = getRemappedInputChannel (i);
+
+ if (remappedChan >= 0 && remappedChan < numChans)
+ {
+ buffer.copyFrom (i, 0, *bufferToFill.buffer,
+ remappedChan,
+ bufferToFill.startSample,
+ bufferToFill.numSamples);
+ }
+ else
+ {
+ buffer.clear (i, 0, bufferToFill.numSamples);
+ }
+ }
+
+ remappedInfo.numSamples = bufferToFill.numSamples;
+
+ source->getNextAudioBlock (remappedInfo);
+
+ bufferToFill.clearActiveBufferRegion();
+
+ for (int i = 0; i < requiredNumberOfChannels; ++i)
+ {
+ const int remappedChan = getRemappedOutputChannel (i);
+
+ if (remappedChan >= 0 && remappedChan < numChans)
+ {
+ bufferToFill.buffer->addFrom (remappedChan, bufferToFill.startSample,
+ buffer, i, 0, bufferToFill.numSamples);
+
+ }
+ }
+}
+
+//==============================================================================
+XmlElement* ChannelRemappingAudioSource::createXml() const
+{
+ XmlElement* e = new XmlElement ("MAPPINGS");
+ String ins, outs;
+
+ const ScopedLock sl (lock);
+
+ for (int i = 0; i < remappedInputs.size(); ++i)
+ ins << remappedInputs.getUnchecked(i) << ' ';
+
+ for (int i = 0; i < remappedOutputs.size(); ++i)
+ outs << remappedOutputs.getUnchecked(i) << ' ';
+
+ e->setAttribute ("inputs", ins.trimEnd());
+ e->setAttribute ("outputs", outs.trimEnd());
+
+ return e;
+}
+
+void ChannelRemappingAudioSource::restoreFromXml (const XmlElement& e)
+{
+ if (e.hasTagName ("MAPPINGS"))
+ {
+ const ScopedLock sl (lock);
+
+ clearAllMappings();
+
+ StringArray ins, outs;
+ ins.addTokens (e.getStringAttribute ("inputs"), false);
+ outs.addTokens (e.getStringAttribute ("outputs"), false);
+
+ for (int i = 0; i < ins.size(); ++i)
+ remappedInputs.add (ins[i].getIntValue());
+
+ for (int i = 0; i < outs.size(); ++i)
+ remappedOutputs.add (outs[i].getIntValue());
+ }
+}
diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.h
new file mode 100644
index 0000000..483fe99
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.h
@@ -0,0 +1,143 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_CHANNELREMAPPINGAUDIOSOURCE_H_INCLUDED
+#define JUCE_CHANNELREMAPPINGAUDIOSOURCE_H_INCLUDED
+
+
+//==============================================================================
+/**
+ An AudioSource that takes the audio from another source, and re-maps its
+ input and output channels to a different arrangement.
+
+ You can use this to increase or decrease the number of channels that an
+ audio source uses, or to re-order those channels.
+
+ Call the reset() method before using it to set up a default mapping, and then
+ the setInputChannelMapping() and setOutputChannelMapping() methods to
+ create an appropriate mapping, otherwise no channels will be connected and
+ it'll produce silence.
+
+ @see AudioSource
+*/
+class ChannelRemappingAudioSource : public AudioSource
+{
+public:
+ //==============================================================================
+ /** Creates a remapping source that will pass on audio from the given input.
+
+ @param source the input source to use. Make sure that this doesn't
+ get deleted before the ChannelRemappingAudioSource object
+ @param deleteSourceWhenDeleted if true, the input source will be deleted
+ when this object is deleted, if false, the caller is
+ responsible for its deletion
+ */
+ ChannelRemappingAudioSource (AudioSource* source,
+ bool deleteSourceWhenDeleted);
+
+ /** Destructor. */
+ ~ChannelRemappingAudioSource();
+
+ //==============================================================================
+ /** Specifies a number of channels that this audio source must produce from its
+ getNextAudioBlock() callback.
+ */
+ void setNumberOfChannelsToProduce (int requiredNumberOfChannels);
+
+ /** Clears any mapped channels.
+
+ After this, no channels are mapped, so this object will produce silence. Create
+ some mappings with setInputChannelMapping() and setOutputChannelMapping().
+ */
+ void clearAllMappings();
+
+ /** Creates an input channel mapping.
+
+ When the getNextAudioBlock() method is called, the data in channel sourceChannelIndex of the incoming
+ data will be sent to destChannelIndex of our input source.
+
+ @param destChannelIndex the index of an input channel in our input audio source (i.e. the
+ source specified when this object was created).
+ @param sourceChannelIndex the index of the input channel in the incoming audio data buffer
+ during our getNextAudioBlock() callback
+ */
+ void setInputChannelMapping (int destChannelIndex,
+ int sourceChannelIndex);
+
+ /** Creates an output channel mapping.
+
+ When the getNextAudioBlock() method is called, the data returned in channel sourceChannelIndex by
+ our input audio source will be copied to channel destChannelIndex of the final buffer.
+
+ @param sourceChannelIndex the index of an output channel coming from our input audio source
+ (i.e. the source specified when this object was created).
+ @param destChannelIndex the index of the output channel in the incoming audio data buffer
+ during our getNextAudioBlock() callback
+ */
+ void setOutputChannelMapping (int sourceChannelIndex,
+ int destChannelIndex);
+
+ /** Returns the channel from our input that will be sent to channel inputChannelIndex of
+ our input audio source.
+ */
+ int getRemappedInputChannel (int inputChannelIndex) const;
+
+ /** Returns the output channel to which channel outputChannelIndex of our input audio
+ source will be sent to.
+ */
+ int getRemappedOutputChannel (int outputChannelIndex) const;
+
+
+ //==============================================================================
+ /** Returns an XML object to encapsulate the state of the mappings.
+ @see restoreFromXml
+ */
+ XmlElement* createXml() const;
+
+ /** Restores the mappings from an XML object created by createXML().
+ @see createXml
+ */
+ void restoreFromXml (const XmlElement&);
+
+ //==============================================================================
+ void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override;
+ void releaseResources() override;
+ void getNextAudioBlock (const AudioSourceChannelInfo&) override;
+
+
+private:
+ //==============================================================================
+ OptionalScopedPointer source;
+ Array remappedInputs, remappedOutputs;
+ int requiredNumberOfChannels;
+
+ AudioSampleBuffer buffer;
+ AudioSourceChannelInfo remappedInfo;
+ CriticalSection lock;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChannelRemappingAudioSource)
+};
+
+
+#endif // JUCE_CHANNELREMAPPINGAUDIOSOURCE_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp
new file mode 100644
index 0000000..08e87f0
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp
@@ -0,0 +1,77 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+IIRFilterAudioSource::IIRFilterAudioSource (AudioSource* const inputSource,
+ const bool deleteInputWhenDeleted)
+ : input (inputSource, deleteInputWhenDeleted)
+{
+ jassert (inputSource != nullptr);
+
+ for (int i = 2; --i >= 0;)
+ iirFilters.add (new IIRFilter());
+}
+
+IIRFilterAudioSource::~IIRFilterAudioSource() {}
+
+//==============================================================================
+void IIRFilterAudioSource::setCoefficients (const IIRCoefficients& newCoefficients)
+{
+ for (int i = iirFilters.size(); --i >= 0;)
+ iirFilters.getUnchecked(i)->setCoefficients (newCoefficients);
+}
+
+void IIRFilterAudioSource::makeInactive()
+{
+ for (int i = iirFilters.size(); --i >= 0;)
+ iirFilters.getUnchecked(i)->makeInactive();
+}
+
+//==============================================================================
+void IIRFilterAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate)
+{
+ input->prepareToPlay (samplesPerBlockExpected, sampleRate);
+
+ for (int i = iirFilters.size(); --i >= 0;)
+ iirFilters.getUnchecked(i)->reset();
+}
+
+void IIRFilterAudioSource::releaseResources()
+{
+ input->releaseResources();
+}
+
+void IIRFilterAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill)
+{
+ input->getNextAudioBlock (bufferToFill);
+
+ const int numChannels = bufferToFill.buffer->getNumChannels();
+
+ while (numChannels > iirFilters.size())
+ iirFilters.add (new IIRFilter (*iirFilters.getUnchecked (0)));
+
+ for (int i = 0; i < numChannels; ++i)
+ iirFilters.getUnchecked(i)
+ ->processSamples (bufferToFill.buffer->getWritePointer (i, bufferToFill.startSample),
+ bufferToFill.numSamples);
+}
diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.h
new file mode 100644
index 0000000..40844c5
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.h
@@ -0,0 +1,70 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_IIRFILTERAUDIOSOURCE_H_INCLUDED
+#define JUCE_IIRFILTERAUDIOSOURCE_H_INCLUDED
+
+
+//==============================================================================
+/**
+ An AudioSource that performs an IIR filter on another source.
+*/
+class JUCE_API IIRFilterAudioSource : public AudioSource
+{
+public:
+ //==============================================================================
+ /** Creates a IIRFilterAudioSource for a given input source.
+
+ @param inputSource the input source to read from - this must not be null
+ @param deleteInputWhenDeleted if true, the input source will be deleted when
+ this object is deleted
+ */
+ IIRFilterAudioSource (AudioSource* inputSource,
+ bool deleteInputWhenDeleted);
+
+ /** Destructor. */
+ ~IIRFilterAudioSource();
+
+ //==============================================================================
+ /** Changes the filter to use the same parameters as the one being passed in. */
+ void setCoefficients (const IIRCoefficients& newCoefficients);
+
+ /** Calls IIRFilter::makeInactive() on all the filters being used internally. */
+ void makeInactive();
+
+ //==============================================================================
+ void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override;
+ void releaseResources() override;
+ void getNextAudioBlock (const AudioSourceChannelInfo&) override;
+
+private:
+ //==============================================================================
+ OptionalScopedPointer input;
+ OwnedArray iirFilters;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (IIRFilterAudioSource)
+};
+
+
+#endif // JUCE_IIRFILTERAUDIOSOURCE_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.cpp
new file mode 100644
index 0000000..60ae191
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.cpp
@@ -0,0 +1,155 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+MixerAudioSource::MixerAudioSource()
+ : currentSampleRate (0.0), bufferSizeExpected (0)
+{
+}
+
+MixerAudioSource::~MixerAudioSource()
+{
+ removeAllInputs();
+}
+
+//==============================================================================
+void MixerAudioSource::addInputSource (AudioSource* input, const bool deleteWhenRemoved)
+{
+ if (input != nullptr && ! inputs.contains (input))
+ {
+ double localRate;
+ int localBufferSize;
+
+ {
+ const ScopedLock sl (lock);
+ localRate = currentSampleRate;
+ localBufferSize = bufferSizeExpected;
+ }
+
+ if (localRate > 0.0)
+ input->prepareToPlay (localBufferSize, localRate);
+
+ const ScopedLock sl (lock);
+
+ inputsToDelete.setBit (inputs.size(), deleteWhenRemoved);
+ inputs.add (input);
+ }
+}
+
+void MixerAudioSource::removeInputSource (AudioSource* const input)
+{
+ if (input != nullptr)
+ {
+ ScopedPointer toDelete;
+
+ {
+ const ScopedLock sl (lock);
+ const int index = inputs.indexOf (input);
+
+ if (index < 0)
+ return;
+
+ if (inputsToDelete [index])
+ toDelete = input;
+
+ inputsToDelete.shiftBits (-1, index);
+ inputs.remove (index);
+ }
+
+ input->releaseResources();
+ }
+}
+
+void MixerAudioSource::removeAllInputs()
+{
+ OwnedArray toDelete;
+
+ {
+ const ScopedLock sl (lock);
+
+ for (int i = inputs.size(); --i >= 0;)
+ if (inputsToDelete[i])
+ toDelete.add (inputs.getUnchecked(i));
+
+ inputs.clear();
+ }
+
+ for (int i = toDelete.size(); --i >= 0;)
+ toDelete.getUnchecked(i)->releaseResources();
+}
+
+void MixerAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate)
+{
+ tempBuffer.setSize (2, samplesPerBlockExpected);
+
+ const ScopedLock sl (lock);
+
+ currentSampleRate = sampleRate;
+ bufferSizeExpected = samplesPerBlockExpected;
+
+ for (int i = inputs.size(); --i >= 0;)
+ inputs.getUnchecked(i)->prepareToPlay (samplesPerBlockExpected, sampleRate);
+}
+
+void MixerAudioSource::releaseResources()
+{
+ const ScopedLock sl (lock);
+
+ for (int i = inputs.size(); --i >= 0;)
+ inputs.getUnchecked(i)->releaseResources();
+
+ tempBuffer.setSize (2, 0);
+
+ currentSampleRate = 0;
+ bufferSizeExpected = 0;
+}
+
+void MixerAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
+{
+ const ScopedLock sl (lock);
+
+ if (inputs.size() > 0)
+ {
+ inputs.getUnchecked(0)->getNextAudioBlock (info);
+
+ if (inputs.size() > 1)
+ {
+ tempBuffer.setSize (jmax (1, info.buffer->getNumChannels()),
+ info.buffer->getNumSamples());
+
+ AudioSourceChannelInfo info2 (&tempBuffer, 0, info.numSamples);
+
+ for (int i = 1; i < inputs.size(); ++i)
+ {
+ inputs.getUnchecked(i)->getNextAudioBlock (info2);
+
+ for (int chan = 0; chan < info.buffer->getNumChannels(); ++chan)
+ info.buffer->addFrom (chan, info.startSample, tempBuffer, chan, 0, info.numSamples);
+ }
+ }
+ }
+ else
+ {
+ info.clearActiveBufferRegion();
+ }
+}
diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.h
new file mode 100644
index 0000000..f581ac3
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.h
@@ -0,0 +1,101 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_MIXERAUDIOSOURCE_H_INCLUDED
+#define JUCE_MIXERAUDIOSOURCE_H_INCLUDED
+
+
+//==============================================================================
+/**
+ An AudioSource that mixes together the output of a set of other AudioSources.
+
+ Input sources can be added and removed while the mixer is running as long as their
+ prepareToPlay() and releaseResources() methods are called before and after adding
+ them to the mixer.
+*/
+class JUCE_API MixerAudioSource : public AudioSource
+{
+public:
+ //==============================================================================
+ /** Creates a MixerAudioSource. */
+ MixerAudioSource();
+
+ /** Destructor. */
+ ~MixerAudioSource();
+
+ //==============================================================================
+ /** Adds an input source to the mixer.
+
+ If the mixer is running you'll need to make sure that the input source
+ is ready to play by calling its prepareToPlay() method before adding it.
+ If the mixer is stopped, then its input sources will be automatically
+ prepared when the mixer's prepareToPlay() method is called.
+
+ @param newInput the source to add to the mixer
+ @param deleteWhenRemoved if true, then this source will be deleted when
+ no longer needed by the mixer.
+ */
+ void addInputSource (AudioSource* newInput, bool deleteWhenRemoved);
+
+ /** Removes an input source.
+ If the source was added by calling addInputSource() with the deleteWhenRemoved
+ flag set, it will be deleted by this method.
+ */
+ void removeInputSource (AudioSource* input);
+
+ /** Removes all the input sources.
+ Any sources which were added by calling addInputSource() with the deleteWhenRemoved
+ flag set will be deleted by this method.
+ */
+ void removeAllInputs();
+
+ //==============================================================================
+ /** Implementation of the AudioSource method.
+ This will call prepareToPlay() on all its input sources.
+ */
+ void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override;
+
+ /** Implementation of the AudioSource method.
+ This will call releaseResources() on all its input sources.
+ */
+ void releaseResources() override;
+
+ /** Implementation of the AudioSource method. */
+ void getNextAudioBlock (const AudioSourceChannelInfo&) override;
+
+
+private:
+ //==============================================================================
+ Array inputs;
+ BigInteger inputsToDelete;
+ CriticalSection lock;
+ AudioSampleBuffer tempBuffer;
+ double currentSampleRate;
+ int bufferSizeExpected;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MixerAudioSource)
+};
+
+
+#endif // JUCE_MIXERAUDIOSOURCE_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_PositionableAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_PositionableAudioSource.h
new file mode 100644
index 0000000..2213722
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_PositionableAudioSource.h
@@ -0,0 +1,78 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_POSITIONABLEAUDIOSOURCE_H_INCLUDED
+#define JUCE_POSITIONABLEAUDIOSOURCE_H_INCLUDED
+
+
+//==============================================================================
+/**
+ A type of AudioSource which can be repositioned.
+
+ The basic AudioSource just streams continuously with no idea of a current
+ time or length, so the PositionableAudioSource is used for a finite stream
+ that has a current read position.
+
+ @see AudioSource, AudioTransportSource
+*/
+class JUCE_API PositionableAudioSource : public AudioSource
+{
+protected:
+ //==============================================================================
+ /** Creates the PositionableAudioSource. */
+ PositionableAudioSource() noexcept {}
+
+public:
+ /** Destructor */
+ ~PositionableAudioSource() {}
+
+ //==============================================================================
+ /** Tells the stream to move to a new position.
+
+ Calling this indicates that the next call to AudioSource::getNextAudioBlock()
+ should return samples from this position.
+
+ Note that this may be called on a different thread to getNextAudioBlock(),
+ so the subclass should make sure it's synchronised.
+ */
+ virtual void setNextReadPosition (int64 newPosition) = 0;
+
+ /** Returns the position from which the next block will be returned.
+
+ @see setNextReadPosition
+ */
+ virtual int64 getNextReadPosition() const = 0;
+
+ /** Returns the total length of the stream (in samples). */
+ virtual int64 getTotalLength() const = 0;
+
+ /** Returns true if this source is actually playing in a loop. */
+ virtual bool isLooping() const = 0;
+
+ /** Tells the source whether you'd like it to play in a loop. */
+ virtual void setLooping (bool shouldLoop) { (void) shouldLoop; }
+};
+
+
+#endif // JUCE_POSITIONABLEAUDIOSOURCE_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp
new file mode 100644
index 0000000..419f008
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp
@@ -0,0 +1,262 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+ResamplingAudioSource::ResamplingAudioSource (AudioSource* const inputSource,
+ const bool deleteInputWhenDeleted,
+ const int numChannels_)
+ : input (inputSource, deleteInputWhenDeleted),
+ ratio (1.0),
+ lastRatio (1.0),
+ bufferPos (0),
+ sampsInBuffer (0),
+ subSampleOffset (0),
+ numChannels (numChannels_)
+{
+ jassert (input != nullptr);
+ zeromem (coefficients, sizeof (coefficients));
+}
+
+ResamplingAudioSource::~ResamplingAudioSource() {}
+
+void ResamplingAudioSource::setResamplingRatio (const double samplesInPerOutputSample)
+{
+ jassert (samplesInPerOutputSample > 0);
+
+ const SpinLock::ScopedLockType sl (ratioLock);
+ ratio = jmax (0.0, samplesInPerOutputSample);
+}
+
+void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate)
+{
+ const SpinLock::ScopedLockType sl (ratioLock);
+
+ input->prepareToPlay (samplesPerBlockExpected, sampleRate);
+
+ buffer.setSize (numChannels, roundToInt (samplesPerBlockExpected * ratio) + 32);
+
+ filterStates.calloc ((size_t) numChannels);
+ srcBuffers.calloc ((size_t) numChannels);
+ destBuffers.calloc ((size_t) numChannels);
+ createLowPass (ratio);
+
+ flushBuffers();
+}
+
+void ResamplingAudioSource::flushBuffers()
+{
+ buffer.clear();
+ bufferPos = 0;
+ sampsInBuffer = 0;
+ subSampleOffset = 0.0;
+ resetFilters();
+}
+
+void ResamplingAudioSource::releaseResources()
+{
+ input->releaseResources();
+ buffer.setSize (numChannels, 0);
+}
+
+void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
+{
+ double localRatio;
+
+ {
+ const SpinLock::ScopedLockType sl (ratioLock);
+ localRatio = ratio;
+ }
+
+ if (lastRatio != localRatio)
+ {
+ createLowPass (localRatio);
+ lastRatio = localRatio;
+ }
+
+ const int sampsNeeded = roundToInt (info.numSamples * localRatio) + 3;
+
+ int bufferSize = buffer.getNumSamples();
+
+ if (bufferSize < sampsNeeded + 8)
+ {
+ bufferPos %= bufferSize;
+ bufferSize = sampsNeeded + 32;
+ buffer.setSize (buffer.getNumChannels(), bufferSize, true, true);
+ }
+
+ bufferPos %= bufferSize;
+
+ int endOfBufferPos = bufferPos + sampsInBuffer;
+ const int channelsToProcess = jmin (numChannels, info.buffer->getNumChannels());
+
+ while (sampsNeeded > sampsInBuffer)
+ {
+ endOfBufferPos %= bufferSize;
+
+ int numToDo = jmin (sampsNeeded - sampsInBuffer,
+ bufferSize - endOfBufferPos);
+
+ AudioSourceChannelInfo readInfo (&buffer, endOfBufferPos, numToDo);
+ input->getNextAudioBlock (readInfo);
+
+ if (localRatio > 1.0001)
+ {
+ // for down-sampling, pre-apply the filter..
+
+ for (int i = channelsToProcess; --i >= 0;)
+ applyFilter (buffer.getWritePointer (i, endOfBufferPos), numToDo, filterStates[i]);
+ }
+
+ sampsInBuffer += numToDo;
+ endOfBufferPos += numToDo;
+ }
+
+ for (int channel = 0; channel < channelsToProcess; ++channel)
+ {
+ destBuffers[channel] = info.buffer->getWritePointer (channel, info.startSample);
+ srcBuffers[channel] = buffer.getReadPointer (channel);
+ }
+
+ int nextPos = (bufferPos + 1) % bufferSize;
+
+ for (int m = info.numSamples; --m >= 0;)
+ {
+ jassert (sampsInBuffer > 0 && nextPos != endOfBufferPos);
+
+ const float alpha = (float) subSampleOffset;
+
+ for (int channel = 0; channel < channelsToProcess; ++channel)
+ *destBuffers[channel]++ = srcBuffers[channel][bufferPos]
+ + alpha * (srcBuffers[channel][nextPos] - srcBuffers[channel][bufferPos]);
+
+ subSampleOffset += localRatio;
+
+ while (subSampleOffset >= 1.0)
+ {
+ if (++bufferPos >= bufferSize)
+ bufferPos = 0;
+
+ --sampsInBuffer;
+
+ nextPos = (bufferPos + 1) % bufferSize;
+ subSampleOffset -= 1.0;
+ }
+ }
+
+ if (localRatio < 0.9999)
+ {
+ // for up-sampling, apply the filter after transposing..
+ for (int i = channelsToProcess; --i >= 0;)
+ applyFilter (info.buffer->getWritePointer (i, info.startSample), info.numSamples, filterStates[i]);
+ }
+ else if (localRatio <= 1.0001 && info.numSamples > 0)
+ {
+ // if the filter's not currently being applied, keep it stoked with the last couple of samples to avoid discontinuities
+ for (int i = channelsToProcess; --i >= 0;)
+ {
+ const float* const endOfBuffer = info.buffer->getReadPointer (i, info.startSample + info.numSamples - 1);
+ FilterState& fs = filterStates[i];
+
+ if (info.numSamples > 1)
+ {
+ fs.y2 = fs.x2 = *(endOfBuffer - 1);
+ }
+ else
+ {
+ fs.y2 = fs.y1;
+ fs.x2 = fs.x1;
+ }
+
+ fs.y1 = fs.x1 = *endOfBuffer;
+ }
+ }
+
+ jassert (sampsInBuffer >= 0);
+}
+
+void ResamplingAudioSource::createLowPass (const double frequencyRatio)
+{
+ const double proportionalRate = (frequencyRatio > 1.0) ? 0.5 / frequencyRatio
+ : 0.5 * frequencyRatio;
+
+ const double n = 1.0 / std::tan (double_Pi * jmax (0.001, proportionalRate));
+ const double nSquared = n * n;
+ const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared);
+
+ setFilterCoefficients (c1,
+ c1 * 2.0f,
+ c1,
+ 1.0,
+ c1 * 2.0 * (1.0 - nSquared),
+ c1 * (1.0 - std::sqrt (2.0) * n + nSquared));
+}
+
+void ResamplingAudioSource::setFilterCoefficients (double c1, double c2, double c3, double c4, double c5, double c6)
+{
+ const double a = 1.0 / c4;
+
+ c1 *= a;
+ c2 *= a;
+ c3 *= a;
+ c5 *= a;
+ c6 *= a;
+
+ coefficients[0] = c1;
+ coefficients[1] = c2;
+ coefficients[2] = c3;
+ coefficients[3] = c4;
+ coefficients[4] = c5;
+ coefficients[5] = c6;
+}
+
+void ResamplingAudioSource::resetFilters()
+{
+ if (filterStates != nullptr)
+ filterStates.clear ((size_t) numChannels);
+}
+
+void ResamplingAudioSource::applyFilter (float* samples, int num, FilterState& fs)
+{
+ while (--num >= 0)
+ {
+ const double in = *samples;
+
+ double out = coefficients[0] * in
+ + coefficients[1] * fs.x1
+ + coefficients[2] * fs.x2
+ - coefficients[4] * fs.y1
+ - coefficients[5] * fs.y2;
+
+ #if JUCE_INTEL
+ if (! (out < -1.0e-8 || out > 1.0e-8))
+ out = 0;
+ #endif
+
+ fs.x2 = fs.x1;
+ fs.x1 = in;
+ fs.y2 = fs.y1;
+ fs.y1 = out;
+
+ *samples++ = (float) out;
+ }
+}
diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h
new file mode 100644
index 0000000..09d27a8
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h
@@ -0,0 +1,107 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_RESAMPLINGAUDIOSOURCE_H_INCLUDED
+#define JUCE_RESAMPLINGAUDIOSOURCE_H_INCLUDED
+
+
+//==============================================================================
+/**
+ A type of AudioSource that takes an input source and changes its sample rate.
+
+ @see AudioSource
+*/
+class JUCE_API ResamplingAudioSource : public AudioSource
+{
+public:
+ //==============================================================================
+ /** Creates a ResamplingAudioSource for a given input source.
+
+ @param inputSource the input source to read from
+ @param deleteInputWhenDeleted if true, the input source will be deleted when
+ this object is deleted
+ @param numChannels the number of channels to process
+ */
+ ResamplingAudioSource (AudioSource* inputSource,
+ bool deleteInputWhenDeleted,
+ int numChannels = 2);
+
+ /** Destructor. */
+ ~ResamplingAudioSource();
+
+ /** Changes the resampling ratio.
+
+ (This value can be changed at any time, even while the source is running).
+
+ @param samplesInPerOutputSample if set to 1.0, the input is passed through; higher
+ values will speed it up; lower values will slow it
+ down. The ratio must be greater than 0
+ */
+ void setResamplingRatio (double samplesInPerOutputSample);
+
+ /** Returns the current resampling ratio.
+
+ This is the value that was set by setResamplingRatio().
+ */
+ 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 releaseResources() override;
+ void getNextAudioBlock (const AudioSourceChannelInfo&) override;
+
+private:
+ //==============================================================================
+ OptionalScopedPointer input;
+ double ratio, lastRatio;
+ AudioSampleBuffer buffer;
+ int bufferPos, sampsInBuffer;
+ double subSampleOffset;
+ double coefficients[6];
+ SpinLock ratioLock;
+ const int numChannels;
+ HeapBlock destBuffers;
+ HeapBlock srcBuffers;
+
+ void setFilterCoefficients (double c1, double c2, double c3, double c4, double c5, double c6);
+ void createLowPass (double proportionalRate);
+
+ struct FilterState
+ {
+ double x1, x2, y1, y2;
+ };
+
+ HeapBlock filterStates;
+ void resetFilters();
+
+ void applyFilter (float* samples, int num, FilterState& fs);
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResamplingAudioSource)
+};
+
+
+#endif // JUCE_RESAMPLINGAUDIOSOURCE_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp
new file mode 100644
index 0000000..d630075
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp
@@ -0,0 +1,80 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+ReverbAudioSource::ReverbAudioSource (AudioSource* const inputSource, const bool deleteInputWhenDeleted)
+ : input (inputSource, deleteInputWhenDeleted),
+ bypass (false)
+{
+ jassert (inputSource != nullptr);
+}
+
+ReverbAudioSource::~ReverbAudioSource() {}
+
+void ReverbAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate)
+{
+ const ScopedLock sl (lock);
+ input->prepareToPlay (samplesPerBlockExpected, sampleRate);
+ reverb.setSampleRate (sampleRate);
+}
+
+void ReverbAudioSource::releaseResources() {}
+
+void ReverbAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill)
+{
+ const ScopedLock sl (lock);
+
+ input->getNextAudioBlock (bufferToFill);
+
+ if (! bypass)
+ {
+ float* const firstChannel = bufferToFill.buffer->getWritePointer (0, bufferToFill.startSample);
+
+ if (bufferToFill.buffer->getNumChannels() > 1)
+ {
+ reverb.processStereo (firstChannel,
+ bufferToFill.buffer->getWritePointer (1, bufferToFill.startSample),
+ bufferToFill.numSamples);
+ }
+ else
+ {
+ reverb.processMono (firstChannel, bufferToFill.numSamples);
+ }
+ }
+}
+
+void ReverbAudioSource::setParameters (const Reverb::Parameters& newParams)
+{
+ const ScopedLock sl (lock);
+ reverb.setParameters (newParams);
+}
+
+void ReverbAudioSource::setBypassed (bool b) noexcept
+{
+ if (bypass != b)
+ {
+ const ScopedLock sl (lock);
+ bypass = b;
+ reverb.reset();
+ }
+}
diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.h
new file mode 100644
index 0000000..6b90e18
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.h
@@ -0,0 +1,76 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_REVERBAUDIOSOURCE_H_INCLUDED
+#define JUCE_REVERBAUDIOSOURCE_H_INCLUDED
+
+
+//==============================================================================
+/**
+ An AudioSource that uses the Reverb class to apply a reverb to another AudioSource.
+
+ @see Reverb
+*/
+class JUCE_API ReverbAudioSource : public AudioSource
+{
+public:
+ /** Creates a ReverbAudioSource to process a given input source.
+
+ @param inputSource the input source to read from - this must not be null
+ @param deleteInputWhenDeleted if true, the input source will be deleted when
+ this object is deleted
+ */
+ ReverbAudioSource (AudioSource* inputSource,
+ bool deleteInputWhenDeleted);
+
+ /** Destructor. */
+ ~ReverbAudioSource();
+
+ //==============================================================================
+ /** Returns the parameters from the reverb. */
+ const Reverb::Parameters& getParameters() const noexcept { return reverb.getParameters(); }
+
+ /** Changes the reverb's parameters. */
+ void setParameters (const Reverb::Parameters& newParams);
+
+ void setBypassed (bool isBypassed) noexcept;
+ bool isBypassed() const noexcept { return bypass; }
+
+ //==============================================================================
+ void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override;
+ void releaseResources() override;
+ void getNextAudioBlock (const AudioSourceChannelInfo&) override;
+
+private:
+ //==============================================================================
+ CriticalSection lock;
+ OptionalScopedPointer input;
+ Reverb reverb;
+ volatile bool bypass;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ReverbAudioSource)
+};
+
+
+#endif // JUCE_REVERBAUDIOSOURCE_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp
new file mode 100644
index 0000000..a0e04ef
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp
@@ -0,0 +1,75 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+ToneGeneratorAudioSource::ToneGeneratorAudioSource()
+ : frequency (1000.0),
+ sampleRate (44100.0),
+ currentPhase (0.0),
+ phasePerSample (0.0),
+ amplitude (0.5f)
+{
+}
+
+ToneGeneratorAudioSource::~ToneGeneratorAudioSource()
+{
+}
+
+//==============================================================================
+void ToneGeneratorAudioSource::setAmplitude (const float newAmplitude)
+{
+ amplitude = newAmplitude;
+}
+
+void ToneGeneratorAudioSource::setFrequency (const double newFrequencyHz)
+{
+ frequency = newFrequencyHz;
+ phasePerSample = 0.0;
+}
+
+//==============================================================================
+void ToneGeneratorAudioSource::prepareToPlay (int /*samplesPerBlockExpected*/, double rate)
+{
+ currentPhase = 0.0;
+ phasePerSample = 0.0;
+ sampleRate = rate;
+}
+
+void ToneGeneratorAudioSource::releaseResources()
+{
+}
+
+void ToneGeneratorAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
+{
+ if (phasePerSample == 0.0)
+ phasePerSample = double_Pi * 2.0 / (sampleRate / frequency);
+
+ for (int i = 0; i < info.numSamples; ++i)
+ {
+ const float sample = amplitude * (float) std::sin (currentPhase);
+ currentPhase += phasePerSample;
+
+ for (int j = info.buffer->getNumChannels(); --j >= 0;)
+ info.buffer->setSample (j, info.startSample + i, sample);
+ }
+}
diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.h
new file mode 100644
index 0000000..5d09ad8
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.h
@@ -0,0 +1,73 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_TONEGENERATORAUDIOSOURCE_H_INCLUDED
+#define JUCE_TONEGENERATORAUDIOSOURCE_H_INCLUDED
+
+
+//==============================================================================
+/**
+ A simple AudioSource that generates a sine wave.
+
+*/
+class JUCE_API ToneGeneratorAudioSource : public AudioSource
+{
+public:
+ //==============================================================================
+ /** Creates a ToneGeneratorAudioSource. */
+ ToneGeneratorAudioSource();
+
+ /** Destructor. */
+ ~ToneGeneratorAudioSource();
+
+ //==============================================================================
+ /** Sets the signal's amplitude. */
+ void setAmplitude (float newAmplitude);
+
+ /** Sets the signal's frequency. */
+ void setFrequency (double newFrequencyHz);
+
+
+ //==============================================================================
+ /** Implementation of the AudioSource method. */
+ void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override;
+
+ /** Implementation of the AudioSource method. */
+ void releaseResources() override;
+
+ /** Implementation of the AudioSource method. */
+ void getNextAudioBlock (const AudioSourceChannelInfo&) override;
+
+
+private:
+ //==============================================================================
+ double frequency, sampleRate;
+ double currentPhase, phasePerSample;
+ float amplitude;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ToneGeneratorAudioSource)
+};
+
+
+#endif // JUCE_TONEGENERATORAUDIOSOURCE_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp b/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp
new file mode 100644
index 0000000..81110ed
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp
@@ -0,0 +1,503 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+SynthesiserSound::SynthesiserSound() {}
+SynthesiserSound::~SynthesiserSound() {}
+
+//==============================================================================
+SynthesiserVoice::SynthesiserVoice()
+ : currentSampleRate (44100.0),
+ currentlyPlayingNote (-1),
+ noteOnTime (0),
+ keyIsDown (false),
+ sostenutoPedalDown (false)
+{
+}
+
+SynthesiserVoice::~SynthesiserVoice()
+{
+}
+
+bool SynthesiserVoice::isPlayingChannel (const int midiChannel) const
+{
+ return currentlyPlayingSound != nullptr
+ && currentlyPlayingSound->appliesToChannel (midiChannel);
+}
+
+void SynthesiserVoice::setCurrentPlaybackSampleRate (const double newRate)
+{
+ currentSampleRate = newRate;
+}
+
+bool SynthesiserVoice::isVoiceActive() const
+{
+ return getCurrentlyPlayingNote() >= 0;
+}
+
+void SynthesiserVoice::clearCurrentNote()
+{
+ currentlyPlayingNote = -1;
+ currentlyPlayingSound = nullptr;
+}
+
+void SynthesiserVoice::aftertouchChanged (int) {}
+
+bool SynthesiserVoice::wasStartedBefore (const SynthesiserVoice& other) const noexcept
+{
+ return noteOnTime < other.noteOnTime;
+}
+
+//==============================================================================
+Synthesiser::Synthesiser()
+ : sampleRate (0),
+ lastNoteOnCounter (0),
+ shouldStealNotes (true)
+{
+ for (int i = 0; i < numElementsInArray (lastPitchWheelValues); ++i)
+ lastPitchWheelValues[i] = 0x2000;
+}
+
+Synthesiser::~Synthesiser()
+{
+}
+
+//==============================================================================
+SynthesiserVoice* Synthesiser::getVoice (const int index) const
+{
+ const ScopedLock sl (lock);
+ return voices [index];
+}
+
+void Synthesiser::clearVoices()
+{
+ const ScopedLock sl (lock);
+ voices.clear();
+}
+
+SynthesiserVoice* Synthesiser::addVoice (SynthesiserVoice* const newVoice)
+{
+ const ScopedLock sl (lock);
+ return voices.add (newVoice);
+}
+
+void Synthesiser::removeVoice (const int index)
+{
+ const ScopedLock sl (lock);
+ voices.remove (index);
+}
+
+void Synthesiser::clearSounds()
+{
+ const ScopedLock sl (lock);
+ sounds.clear();
+}
+
+SynthesiserSound* Synthesiser::addSound (const SynthesiserSound::Ptr& newSound)
+{
+ const ScopedLock sl (lock);
+ return sounds.add (newSound);
+}
+
+void Synthesiser::removeSound (const int index)
+{
+ const ScopedLock sl (lock);
+ sounds.remove (index);
+}
+
+void Synthesiser::setNoteStealingEnabled (const bool shouldSteal)
+{
+ shouldStealNotes = shouldSteal;
+}
+
+//==============================================================================
+void Synthesiser::setCurrentPlaybackSampleRate (const double newRate)
+{
+ if (sampleRate != newRate)
+ {
+ const ScopedLock sl (lock);
+
+ allNotesOff (0, false);
+
+ sampleRate = newRate;
+
+ for (int i = voices.size(); --i >= 0;)
+ voices.getUnchecked (i)->setCurrentPlaybackSampleRate (newRate);
+ }
+}
+
+void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBuffer& midiData,
+ int startSample, int numSamples)
+{
+ // must set the sample rate before using this!
+ jassert (sampleRate != 0);
+
+ const ScopedLock sl (lock);
+
+ MidiBuffer::Iterator midiIterator (midiData);
+ midiIterator.setNextSamplePosition (startSample);
+ MidiMessage m (0xf4, 0.0);
+
+ while (numSamples > 0)
+ {
+ int midiEventPos;
+ const bool useEvent = midiIterator.getNextEvent (m, midiEventPos)
+ && midiEventPos < startSample + numSamples;
+
+ const int numThisTime = useEvent ? midiEventPos - startSample
+ : numSamples;
+
+ if (numThisTime > 0)
+ renderVoices (outputBuffer, startSample, numThisTime);
+
+ if (useEvent)
+ handleMidiEvent (m);
+
+ startSample += numThisTime;
+ numSamples -= numThisTime;
+ }
+}
+
+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)
+{
+ if (m.isNoteOn())
+ {
+ noteOn (m.getChannel(), m.getNoteNumber(), m.getFloatVelocity());
+ }
+ else if (m.isNoteOff())
+ {
+ noteOff (m.getChannel(), m.getNoteNumber(), m.getFloatVelocity(), true);
+ }
+ else if (m.isAllNotesOff() || m.isAllSoundOff())
+ {
+ allNotesOff (m.getChannel(), true);
+ }
+ else if (m.isPitchWheel())
+ {
+ const int channel = m.getChannel();
+ const int wheelPos = m.getPitchWheelValue();
+ lastPitchWheelValues [channel - 1] = wheelPos;
+
+ handlePitchWheel (channel, wheelPos);
+ }
+ else if (m.isAftertouch())
+ {
+ handleAftertouch (m.getChannel(), m.getNoteNumber(), m.getAfterTouchValue());
+ }
+ else if (m.isController())
+ {
+ handleController (m.getChannel(), m.getControllerNumber(), m.getControllerValue());
+ }
+}
+
+//==============================================================================
+void Synthesiser::noteOn (const int midiChannel,
+ const int midiNoteNumber,
+ const float velocity)
+{
+ const ScopedLock sl (lock);
+
+ for (int i = sounds.size(); --i >= 0;)
+ {
+ SynthesiserSound* const sound = sounds.getUnchecked(i);
+
+ if (sound->appliesToNote (midiNoteNumber)
+ && sound->appliesToChannel (midiChannel))
+ {
+ // If hitting a note that's still ringing, stop it first (it could be
+ // still playing because of the sustain or sostenuto pedal).
+ for (int j = voices.size(); --j >= 0;)
+ {
+ SynthesiserVoice* const voice = voices.getUnchecked (j);
+
+ if (voice->getCurrentlyPlayingNote() == midiNoteNumber
+ && voice->isPlayingChannel (midiChannel))
+ stopVoice (voice, 1.0f, true);
+ }
+
+ startVoice (findFreeVoice (sound, midiChannel, midiNoteNumber, shouldStealNotes),
+ sound, midiChannel, midiNoteNumber, velocity);
+ }
+ }
+}
+
+void Synthesiser::startVoice (SynthesiserVoice* const voice,
+ SynthesiserSound* const sound,
+ const int midiChannel,
+ const int midiNoteNumber,
+ const float velocity)
+{
+ if (voice != nullptr && sound != nullptr)
+ {
+ if (voice->currentlyPlayingSound != nullptr)
+ voice->stopNote (0.0f, false);
+
+ voice->startNote (midiNoteNumber, velocity, sound,
+ lastPitchWheelValues [midiChannel - 1]);
+
+ voice->currentlyPlayingNote = midiNoteNumber;
+ voice->noteOnTime = ++lastNoteOnCounter;
+ voice->currentlyPlayingSound = sound;
+ voice->keyIsDown = true;
+ voice->sostenutoPedalDown = false;
+ }
+}
+
+void Synthesiser::stopVoice (SynthesiserVoice* voice, float velocity, const bool allowTailOff)
+{
+ jassert (voice != nullptr);
+
+ voice->stopNote (velocity, allowTailOff);
+
+ // the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()!
+ jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == 0));
+}
+
+void Synthesiser::noteOff (const int midiChannel,
+ const int midiNoteNumber,
+ const float velocity,
+ const bool allowTailOff)
+{
+ const ScopedLock sl (lock);
+
+ for (int i = voices.size(); --i >= 0;)
+ {
+ SynthesiserVoice* const voice = voices.getUnchecked (i);
+
+ if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
+ {
+ if (SynthesiserSound* const sound = voice->getCurrentlyPlayingSound())
+ {
+ if (sound->appliesToNote (midiNoteNumber)
+ && sound->appliesToChannel (midiChannel))
+ {
+ voice->keyIsDown = false;
+
+ if (! (sustainPedalsDown [midiChannel] || voice->sostenutoPedalDown))
+ stopVoice (voice, velocity, allowTailOff);
+ }
+ }
+ }
+ }
+}
+
+void Synthesiser::allNotesOff (const int midiChannel, const bool allowTailOff)
+{
+ const ScopedLock sl (lock);
+
+ for (int i = voices.size(); --i >= 0;)
+ {
+ SynthesiserVoice* const voice = voices.getUnchecked (i);
+
+ if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
+ voice->stopNote (1.0f, allowTailOff);
+ }
+
+ sustainPedalsDown.clear();
+}
+
+void Synthesiser::handlePitchWheel (const int midiChannel, const int wheelValue)
+{
+ const ScopedLock sl (lock);
+
+ for (int i = voices.size(); --i >= 0;)
+ {
+ SynthesiserVoice* const voice = voices.getUnchecked (i);
+
+ if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
+ voice->pitchWheelMoved (wheelValue);
+ }
+}
+
+void Synthesiser::handleController (const int midiChannel,
+ const int controllerNumber,
+ const int controllerValue)
+{
+ switch (controllerNumber)
+ {
+ case 0x40: handleSustainPedal (midiChannel, controllerValue >= 64); break;
+ case 0x42: handleSostenutoPedal (midiChannel, controllerValue >= 64); break;
+ case 0x43: handleSoftPedal (midiChannel, controllerValue >= 64); break;
+ default: break;
+ }
+
+ const ScopedLock sl (lock);
+
+ for (int i = voices.size(); --i >= 0;)
+ {
+ SynthesiserVoice* const voice = voices.getUnchecked (i);
+
+ if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
+ voice->controllerMoved (controllerNumber, controllerValue);
+ }
+}
+
+void Synthesiser::handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue)
+{
+ const ScopedLock sl (lock);
+
+ for (int i = voices.size(); --i >= 0;)
+ {
+ SynthesiserVoice* const voice = voices.getUnchecked (i);
+
+ if (voice->getCurrentlyPlayingNote() == midiNoteNumber
+ && (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)))
+ voice->aftertouchChanged (aftertouchValue);
+ }
+}
+
+void Synthesiser::handleSustainPedal (int midiChannel, bool isDown)
+{
+ jassert (midiChannel > 0 && midiChannel <= 16);
+ const ScopedLock sl (lock);
+
+ if (isDown)
+ {
+ sustainPedalsDown.setBit (midiChannel);
+ }
+ else
+ {
+ for (int i = voices.size(); --i >= 0;)
+ {
+ SynthesiserVoice* const voice = voices.getUnchecked (i);
+
+ if (voice->isPlayingChannel (midiChannel) && ! voice->keyIsDown)
+ stopVoice (voice, 1.0f, true);
+ }
+
+ sustainPedalsDown.clearBit (midiChannel);
+ }
+}
+
+void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown)
+{
+ jassert (midiChannel > 0 && midiChannel <= 16);
+ const ScopedLock sl (lock);
+
+ for (int i = voices.size(); --i >= 0;)
+ {
+ SynthesiserVoice* const voice = voices.getUnchecked (i);
+
+ if (voice->isPlayingChannel (midiChannel))
+ {
+ if (isDown)
+ voice->sostenutoPedalDown = true;
+ else if (voice->sostenutoPedalDown)
+ stopVoice (voice, 1.0f, true);
+ }
+ }
+}
+
+void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/)
+{
+ (void) midiChannel;
+ jassert (midiChannel > 0 && midiChannel <= 16);
+}
+
+//==============================================================================
+SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay,
+ int midiChannel, int midiNoteNumber,
+ const bool stealIfNoneAvailable) const
+{
+ const ScopedLock sl (lock);
+
+ for (int i = 0; i < voices.size(); ++i)
+ {
+ SynthesiserVoice* const voice = voices.getUnchecked (i);
+
+ if ((! voice->isVoiceActive()) && voice->canPlaySound (soundToPlay))
+ return voice;
+ }
+
+ if (stealIfNoneAvailable)
+ return findVoiceToSteal (soundToPlay, midiChannel, midiNoteNumber);
+
+ return nullptr;
+}
+
+struct VoiceAgeSorter
+{
+ static int compareElements (SynthesiserVoice* v1, SynthesiserVoice* v2) noexcept
+ {
+ 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 usableVoices;
+ usableVoices.ensureStorageAllocated (voices.size());
+
+ for (int i = 0; i < voices.size(); ++i)
+ {
+ SynthesiserVoice* const voice = voices.getUnchecked (i);
+
+ if (voice->canPlaySound (soundToPlay))
+ {
+ 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 (bottom != nullptr && top != nullptr);
+
+ // 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;
+}
diff --git a/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h b/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h
new file mode 100644
index 0000000..19bb7a4
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h
@@ -0,0 +1,557 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_SYNTHESISER_H_INCLUDED
+#define JUCE_SYNTHESISER_H_INCLUDED
+
+
+//==============================================================================
+/**
+ Describes one of the sounds that a Synthesiser can play.
+
+ A synthesiser can contain one or more sounds, and a sound can choose which
+ midi notes and channels can trigger it.
+
+ The SynthesiserSound is a passive class that just describes what the sound is -
+ the actual audio rendering for a sound is done by a SynthesiserVoice. This allows
+ more than one SynthesiserVoice to play the same sound at the same time.
+
+ @see Synthesiser, SynthesiserVoice
+*/
+class JUCE_API SynthesiserSound : public ReferenceCountedObject
+{
+protected:
+ //==============================================================================
+ SynthesiserSound();
+
+public:
+ /** Destructor. */
+ virtual ~SynthesiserSound();
+
+ //==============================================================================
+ /** Returns true if this sound should be played when a given midi note is pressed.
+
+ The Synthesiser will use this information when deciding which sounds to trigger
+ for a given note.
+ */
+ virtual bool appliesToNote (int midiNoteNumber) = 0;
+
+ /** 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
+ for a given note.
+ */
+ virtual bool appliesToChannel (int midiChannel) = 0;
+
+ /** The class is reference-counted, so this is a handy pointer class for it. */
+ typedef ReferenceCountedObjectPtr Ptr;
+
+
+private:
+ //==============================================================================
+ JUCE_LEAK_DETECTOR (SynthesiserSound)
+};
+
+
+//==============================================================================
+/**
+ Represents a voice that a Synthesiser can use to play a SynthesiserSound.
+
+ A voice plays a single sound at a time, and a synthesiser holds an array of
+ voices so that it can play polyphonically.
+
+ @see Synthesiser, SynthesiserSound
+*/
+class JUCE_API SynthesiserVoice
+{
+public:
+ //==============================================================================
+ /** Creates a voice. */
+ SynthesiserVoice();
+
+ /** Destructor. */
+ virtual ~SynthesiserVoice();
+
+ //==============================================================================
+ /** Returns the midi note that this voice is currently playing.
+ Returns a value less than 0 if no note is playing.
+ */
+ int getCurrentlyPlayingNote() const noexcept { return currentlyPlayingNote; }
+
+ /** Returns the sound that this voice is currently playing.
+ Returns nullptr if it's not playing.
+ */
+ SynthesiserSound::Ptr getCurrentlyPlayingSound() const noexcept { return currentlyPlayingSound; }
+
+ /** Must return true if this voice object is capable of playing the given sound.
+
+ If there are different classes of sound, and different classes of voice, a voice can
+ choose which ones it wants to take on.
+
+ A typical implementation of this method may just return true if there's only one type
+ of voice and sound, or it might check the type of the sound object passed-in and
+ see if it's one that it understands.
+ */
+ virtual bool canPlaySound (SynthesiserSound*) = 0;
+
+ /** Called to start a new note.
+ This will be called during the rendering callback, so must be fast and thread-safe.
+ */
+ virtual void startNote (int midiNoteNumber,
+ float velocity,
+ SynthesiserSound* sound,
+ int currentPitchWheelPosition) = 0;
+
+ /** Called to stop a note.
+
+ 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
+ sound immediately, and must call clearCurrentNote() to reset the state of this voice
+ and allow the synth to reassign it another sound.
+
+ If allowTailOff is true and the voice decides to do a tail-off, then it's allowed to
+ begin fading out its sound, and it can stop playing until it's finished. As soon as it
+ finishes playing (during the rendering callback), it must make sure that it calls
+ clearCurrentNote().
+ */
+ virtual void stopNote (float velocity, bool allowTailOff) = 0;
+
+ /** Returns true if this voice is currently busy playing a sound.
+ By default this just checks the getCurrentlyPlayingNote() value, but can
+ be overridden for more advanced checking.
+ */
+ virtual bool isVoiceActive() const;
+
+ /** Called to let the voice know that the pitch wheel has been moved.
+ This will be called during the rendering callback, so must be fast and thread-safe.
+ */
+ virtual void pitchWheelMoved (int newPitchWheelValue) = 0;
+
+ /** Called to let the voice know that a midi controller has been moved.
+ This will be called during the rendering callback, so must be fast and thread-safe.
+ */
+ virtual void controllerMoved (int controllerNumber, int newControllerValue) = 0;
+
+ /** Called to let the voice know that the aftertouch has changed.
+ This will be called during the rendering callback, so must be fast and thread-safe.
+ */
+ virtual void aftertouchChanged (int newAftertouchValue);
+
+ //==============================================================================
+ /** Renders the next block of data for this voice.
+
+ The output audio data must be added to the current contents of the buffer provided.
+ Only the region of the buffer between startSample and (startSample + numSamples)
+ should be altered by this method.
+
+ If the voice is currently silent, it should just return without doing anything.
+
+ If the sound that the voice is playing finishes during the course of this rendered
+ block, it must call clearCurrentNote(), to tell the synthesiser that it has finished.
+
+ The size of the blocks that are rendered can change each time it is called, and may
+ involve rendering as little as 1 sample at a time. In between rendering callbacks,
+ the voice's methods will be called to tell it about note and controller events.
+ */
+ virtual void renderNextBlock (AudioSampleBuffer& outputBuffer,
+ int startSample,
+ int numSamples) = 0;
+
+ /** Changes the voice's reference sample rate.
+
+ The rate is set so that subclasses know the output rate and can set their pitch
+ accordingly.
+
+ This method is called by the synth, and subclasses can access the current rate with
+ the currentSampleRate member.
+ */
+ virtual void setCurrentPlaybackSampleRate (double newRate);
+
+ /** 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.
+ */
+ virtual bool isPlayingChannel (int midiChannel) const;
+
+ /** Returns the current target sample rate at which rendering is being done.
+ Subclasses may need to know this so that they can pitch things correctly.
+ */
+ double getSampleRate() const noexcept { return currentSampleRate; }
+
+ /** Returns true if the key that triggered this voice is still held down.
+ Note that the voice may still be playing after the key was released (e.g because the
+ sostenuto pedal is down).
+ */
+ bool isKeyDown() const noexcept { return keyIsDown; }
+
+ /** Returns true if the sostenuto pedal is currently active for this voice. */
+ bool isSostenutoPedalDown() const noexcept { return sostenutoPedalDown; }
+
+ /** Returns true if this voice started playing its current note before the other voice did. */
+ bool wasStartedBefore (const SynthesiserVoice& other) const noexcept;
+
+protected:
+ /** 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
+ to play new ones.
+
+ It must either call it in the stopNote() method, or if the voice is tailing off,
+ then it should call it later during the renderNextBlock method, as soon as it
+ finishes its tail-off.
+
+ It can also be called at any time during the render callback if the sound happens
+ to have finished, e.g. if it's playing a sample and the sample finishes.
+ */
+ void clearCurrentNote();
+
+
+private:
+ //==============================================================================
+ friend class Synthesiser;
+
+ double currentSampleRate;
+ int currentlyPlayingNote;
+ uint32 noteOnTime;
+ SynthesiserSound::Ptr currentlyPlayingSound;
+ 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)
+};
+
+
+//==============================================================================
+/**
+ Base class for a musical device that can play sounds.
+
+ To create a synthesiser, you'll need to create a subclass of SynthesiserSound
+ to describe each sound available to your synth, and a subclass of SynthesiserVoice
+ which can play back one of these sounds.
+
+ Then you can use the addVoice() and addSound() methods to give the synthesiser a
+ set of sounds, and a set of voices it can use to play them. If you only give it
+ one voice it will be monophonic - the more voices it has, the more polyphony it'll
+ have available.
+
+ Then repeatedly call the renderNextBlock() method to produce the audio. Any midi
+ events that go in will be scanned for note on/off messages, and these are used to
+ start and stop the voices playing the appropriate sounds.
+
+ While it's playing, you can also cause notes to be triggered by calling the noteOn(),
+ noteOff() and other controller methods.
+
+ Before rendering, be sure to call the setCurrentPlaybackSampleRate() to tell it
+ what the target playback rate is. This value is passed on to the voices so that
+ they can pitch their output correctly.
+*/
+class JUCE_API Synthesiser
+{
+public:
+ //==============================================================================
+ /** Creates a new synthesiser.
+ You'll need to add some sounds and voices before it'll make any sound.
+ */
+ Synthesiser();
+
+ /** Destructor. */
+ virtual ~Synthesiser();
+
+ //==============================================================================
+ /** Deletes all voices. */
+ void clearVoices();
+
+ /** Returns the number of voices that have been added. */
+ int getNumVoices() const noexcept { return voices.size(); }
+
+ /** Returns one of the voices that have been added. */
+ SynthesiserVoice* getVoice (int index) const;
+
+ /** Adds a new voice to the synth.
+
+ All the voices should be the same class of object and are treated equally.
+
+ The object passed in will be managed by the synthesiser, which will delete
+ it later on when no longer needed. The caller should not retain a pointer to the
+ voice.
+ */
+ SynthesiserVoice* addVoice (SynthesiserVoice* newVoice);
+
+ /** Deletes one of the voices. */
+ void removeVoice (int index);
+
+ //==============================================================================
+ /** Deletes all sounds. */
+ void clearSounds();
+
+ /** Returns the number of sounds that have been added to the synth. */
+ int getNumSounds() const noexcept { return sounds.size(); }
+
+ /** Returns one of the sounds. */
+ SynthesiserSound* getSound (int index) const noexcept { return sounds [index]; }
+
+ /** Adds a new sound to the synthesiser.
+
+ The object passed in is reference counted, so will be deleted when the
+ synthesiser and all voices are no longer using it.
+ */
+ SynthesiserSound* addSound (const SynthesiserSound::Ptr& newSound);
+
+ /** Removes and deletes one of the sounds. */
+ void removeSound (int index);
+
+ //==============================================================================
+ /** If set to true, then the synth will try to take over an existing voice if
+ it runs out and needs to play another note.
+
+ The value of this boolean is passed into findFreeVoice(), so the result will
+ depend on the implementation of this method.
+ */
+ void setNoteStealingEnabled (bool shouldStealNotes);
+
+ /** Returns true if note-stealing is enabled.
+ @see setNoteStealingEnabled
+ */
+ bool isNoteStealingEnabled() const noexcept { return shouldStealNotes; }
+
+ //==============================================================================
+ /** Triggers a note-on event.
+
+ The default method here will find all the sounds that want to be triggered by
+ this note/channel. For each sound, it'll try to find a free voice, and use the
+ voice to start playing the sound.
+
+ Subclasses might want to override this if they need a more complex algorithm.
+
+ This method will be called automatically according to the midi data passed into
+ renderNextBlock(), but may be called explicitly too.
+
+ The midiChannel parameter is the channel, between 1 and 16 inclusive.
+ */
+ virtual void noteOn (int midiChannel,
+ int midiNoteNumber,
+ float velocity);
+
+ /** Triggers a note-off event.
+
+ This will turn off any voices that are playing a sound for the given note/channel.
+
+ If allowTailOff is true, the voices will be allowed to fade out the notes gracefully
+ (if they can do). If this is false, the notes will all be cut off immediately.
+
+ This method will be called automatically according to the midi data passed into
+ renderNextBlock(), but may be called explicitly too.
+
+ The midiChannel parameter is the channel, between 1 and 16 inclusive.
+ */
+ virtual void noteOff (int midiChannel,
+ int midiNoteNumber,
+ float velocity,
+ bool allowTailOff);
+
+ /** Turns off all notes.
+
+ This will turn off any voices that are playing a sound on the given midi channel.
+
+ If midiChannel is 0 or less, then all voices will be turned off, regardless of
+ which channel they're playing. Otherwise it represents a valid midi channel, from
+ 1 to 16 inclusive.
+
+ If allowTailOff is true, the voices will be allowed to fade out the notes gracefully
+ (if they can do). If this is false, the notes will all be cut off immediately.
+
+ This method will be called automatically according to the midi data passed into
+ renderNextBlock(), but may be called explicitly too.
+ */
+ virtual void allNotesOff (int midiChannel,
+ bool allowTailOff);
+
+ /** Sends a pitch-wheel message to any active voices.
+
+ This will send a pitch-wheel message to any voices that are playing sounds on
+ the given midi channel.
+
+ This method will be called automatically according to the midi data passed into
+ renderNextBlock(), but may be called explicitly too.
+
+ @param midiChannel the midi channel, from 1 to 16 inclusive
+ @param wheelValue the wheel position, from 0 to 0x3fff, as returned by MidiMessage::getPitchWheelValue()
+ */
+ virtual void handlePitchWheel (int midiChannel,
+ int wheelValue);
+
+ /** Sends a midi controller message to any active voices.
+
+ This will send a midi controller message to any voices that are playing sounds on
+ the given midi channel.
+
+ This method will be called automatically according to the midi data passed into
+ renderNextBlock(), but may be called explicitly too.
+
+ @param midiChannel the midi channel, from 1 to 16 inclusive
+ @param controllerNumber the midi controller type, as returned by MidiMessage::getControllerNumber()
+ @param controllerValue the midi controller value, between 0 and 127, as returned by MidiMessage::getControllerValue()
+ */
+ virtual void handleController (int midiChannel,
+ int controllerNumber,
+ int controllerValue);
+
+ /** Sends an aftertouch message.
+
+ This will send an aftertouch message to any voices that are playing sounds on
+ the given midi channel and note number.
+
+ This method will be called automatically according to the midi data passed into
+ renderNextBlock(), but may be called explicitly too.
+
+ @param midiChannel the midi channel, from 1 to 16 inclusive
+ @param midiNoteNumber the midi note number, 0 to 127
+ @param aftertouchValue the aftertouch value, between 0 and 127,
+ as returned by MidiMessage::getAftertouchValue()
+ */
+ virtual void handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue);
+
+ /** Handles a sustain pedal event. */
+ virtual void handleSustainPedal (int midiChannel, bool isDown);
+
+ /** Handles a sostenuto pedal event. */
+ virtual void handleSostenutoPedal (int midiChannel, bool isDown);
+
+ /** Can be overridden to handle soft pedal events. */
+ virtual void handleSoftPedal (int midiChannel, bool isDown);
+
+ //==============================================================================
+ /** Tells the synthesiser what the sample rate is for the audio it's being used to render.
+
+ This value is propagated to the voices so that they can use it to render the correct
+ pitches.
+ */
+ virtual void setCurrentPlaybackSampleRate (double sampleRate);
+
+ /** Creates the next block of audio output.
+
+ This will process the next numSamples of data from all the voices, and add that output
+ to the audio block supplied, starting from the offset specified. Note that the
+ data will be added to the current contents of the buffer, so you should clear it
+ before calling this method if necessary.
+
+ The midi events in the inputMidi buffer are parsed for note and controller events,
+ and these are used to trigger the voices. Note that the startSample offset applies
+ both to the audio output buffer and the midi input buffer, so any midi events
+ with timestamps outside the specified region will be ignored.
+ */
+ void renderNextBlock (AudioSampleBuffer& outputAudio,
+ const MidiBuffer& inputMidi,
+ int startSample,
+ 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:
+ //==============================================================================
+ /** This is used to control access to the rendering callback and the note trigger methods. */
+ CriticalSection lock;
+
+ OwnedArray voices;
+ ReferenceCountedArray sounds;
+
+ /** The last pitch-wheel values for each midi channel. */
+ int lastPitchWheelValues [16];
+
+ /** Renders the voices for the given range.
+ 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.
+
+ To implement a custom note-stealing algorithm, you can either override this
+ method, or (preferably) override findVoiceToSteal().
+ */
+ virtual SynthesiserVoice* findFreeVoice (SynthesiserSound* soundToPlay,
+ int midiChannel,
+ int midiNoteNumber,
+ bool stealIfNoneAvailable) const;
+
+ /** Chooses a voice that is most suitable for being re-used.
+ The default method will attempt to find the oldest voice that isn't the
+ 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,
+ int midiChannel,
+ int midiNoteNumber) const;
+
+ /** Starts a specified voice playing a particular sound.
+
+ You'll probably never need to call this, it's used internally by noteOn(), but
+ may be needed by subclasses for custom behaviours.
+ */
+ void startVoice (SynthesiserVoice* voice,
+ SynthesiserSound* sound,
+ int midiChannel,
+ int midiNoteNumber,
+ float velocity);
+
+ /** Can be overridden to do custom handling of incoming midi events. */
+ virtual void handleMidiEvent (const MidiMessage&);
+
+private:
+ //==============================================================================
+ double sampleRate;
+ uint32 lastNoteOnCounter;
+ bool shouldStealNotes;
+ BigInteger sustainPedalsDown;
+
+ void stopVoice (SynthesiserVoice*, float velocity, bool allowTailOff);
+
+ #if JUCE_CATCH_DEPRECATED_CODE_MISUSE
+ // Note the new parameters for these methods.
+ 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
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Synthesiser)
+};
+
+
+#endif // JUCE_SYNTHESISER_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDBurner.h b/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDBurner.h
new file mode 100644
index 0000000..88b1ef2
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDBurner.h
@@ -0,0 +1,169 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_AUDIOCDBURNER_H_INCLUDED
+#define JUCE_AUDIOCDBURNER_H_INCLUDED
+
+#if JUCE_USE_CDBURNER || DOXYGEN
+
+
+//==============================================================================
+/**
+*/
+class AudioCDBurner : public ChangeBroadcaster
+{
+public:
+ //==============================================================================
+ /** Returns a list of available optical drives.
+
+ Use openDevice() to open one of the items from this list.
+ */
+ static StringArray findAvailableDevices();
+
+ /** Tries to open one of the optical drives.
+
+ The deviceIndex is an index into the array returned by findAvailableDevices().
+ */
+ static AudioCDBurner* openDevice (const int deviceIndex);
+
+ /** Destructor. */
+ ~AudioCDBurner();
+
+ //==============================================================================
+ enum DiskState
+ {
+ unknown, /**< An error condition, if the device isn't responding. */
+ trayOpen, /**< The drive is currently open. Note that a slot-loading drive
+ may seem to be permanently open. */
+ noDisc, /**< The drive has no disk in it. */
+ writableDiskPresent, /**< The drive contains a writeable disk. */
+ readOnlyDiskPresent /**< The drive contains a read-only disk. */
+ };
+
+ /** Returns the current status of the device.
+
+ To get informed when the drive's status changes, attach a ChangeListener to
+ the AudioCDBurner.
+ */
+ DiskState getDiskState() const;
+
+ /** Returns true if there's a writable disk in the drive. */
+ bool isDiskPresent() const;
+
+ /** Sends an eject signal to the drive.
+ The eject will happen asynchronously, so you can use getDiskState() and
+ waitUntilStateChange() to monitor its progress.
+ */
+ bool openTray();
+
+ /** Blocks the current thread until the drive's state changes, or until the timeout expires.
+ @returns the device's new state
+ */
+ DiskState waitUntilStateChange (int timeOutMilliseconds);
+
+ //==============================================================================
+ /** Returns the set of possible write speeds that the device can handle.
+ These are as a multiple of 'normal' speed, so e.g. '24x' returns 24, etc.
+ Note that if there's no media present in the drive, this value may be unavailable!
+ @see setWriteSpeed, getWriteSpeed
+ */
+ Array getAvailableWriteSpeeds() const;
+
+ //==============================================================================
+ /** Tries to enable or disable buffer underrun safety on devices that support it.
+ @returns true if it's now enabled. If the device doesn't support it, this
+ will always return false.
+ */
+ bool setBufferUnderrunProtection (bool shouldBeEnabled);
+
+ //==============================================================================
+ /** Returns the number of free blocks on the disk.
+
+ There are 75 blocks per second, at 44100Hz.
+ */
+ int getNumAvailableAudioBlocks() const;
+
+ /** Adds a track to be written.
+
+ The source passed-in here will be kept by this object, and it will
+ be used and deleted at some point in the future, either during the
+ burn() method or when this AudioCDBurner object is deleted. Your caller
+ method shouldn't keep a reference to it or use it again after passing
+ it in here.
+ */
+ bool addAudioTrack (AudioSource* source, int numSamples);
+
+ //==============================================================================
+ /** Receives progress callbacks during a cd-burn operation.
+ @see AudioCDBurner::burn()
+ */
+ class BurnProgressListener
+ {
+ public:
+ BurnProgressListener() noexcept {}
+ virtual ~BurnProgressListener() {}
+
+ /** Called at intervals to report on the progress of the AudioCDBurner.
+
+ To cancel the burn, return true from this method.
+ */
+ virtual bool audioCDBurnProgress (float proportionComplete) = 0;
+ };
+
+ /** Runs the burn process.
+ This method will block until the operation is complete.
+
+ @param listener the object to receive callbacks about progress
+ @param ejectDiscAfterwards whether to eject the disk after the burn completes
+ @param performFakeBurnForTesting if true, no data will actually be written to the disk
+ @param writeSpeed one of the write speeds from getAvailableWriteSpeeds(), or
+ 0 or less to mean the fastest speed.
+ */
+ String burn (BurnProgressListener* listener,
+ bool ejectDiscAfterwards,
+ bool performFakeBurnForTesting,
+ int writeSpeed);
+
+ /** If a burn operation is currently in progress, this tells it to stop
+ as soon as possible.
+
+ It's also possible to stop the burn process by returning true from
+ BurnProgressListener::audioCDBurnProgress()
+ */
+ void abortBurn();
+
+private:
+ //==============================================================================
+ AudioCDBurner (const int deviceIndex);
+
+ class Pimpl;
+ friend struct ContainerDeletePolicy;
+ ScopedPointer pimpl;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioCDBurner)
+};
+
+
+#endif
+#endif // JUCE_AUDIOCDBURNER_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.cpp b/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.cpp
new file mode 100644
index 0000000..ca9c5b0
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.cpp
@@ -0,0 +1,57 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#if JUCE_USE_CDREADER
+
+int AudioCDReader::getNumTracks() const
+{
+ return trackStartSamples.size() - 1;
+}
+
+int AudioCDReader::getPositionOfTrackStart (int trackNum) const
+{
+ return trackStartSamples [trackNum];
+}
+
+const Array& AudioCDReader::getTrackOffsets() const
+{
+ return trackStartSamples;
+}
+
+int AudioCDReader::getCDDBId()
+{
+ int checksum = 0;
+ const int numTracks = getNumTracks();
+
+ for (int i = 0; i < numTracks; ++i)
+ for (int offset = (trackStartSamples.getUnchecked(i) + 88200) / 44100; offset > 0; offset /= 10)
+ checksum += offset % 10;
+
+ const int length = (trackStartSamples.getLast() - trackStartSamples.getFirst()) / 44100;
+
+ // CCLLLLTT: checksum, length, tracks
+ return ((checksum & 0xff) << 24) | (length << 8) | numTracks;
+}
+
+#endif
diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.h b/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.h
new file mode 100644
index 0000000..0e868db
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.h
@@ -0,0 +1,174 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_AUDIOCDREADER_H_INCLUDED
+#define JUCE_AUDIOCDREADER_H_INCLUDED
+
+#if JUCE_USE_CDREADER || DOXYGEN
+
+
+//==============================================================================
+/**
+ A type of AudioFormatReader that reads from an audio CD.
+
+ One of these can be used to read a CD as if it's one big audio stream. Use the
+ getPositionOfTrackStart() method to find where the individual tracks are
+ within the stream.
+
+ @see AudioFormatReader
+*/
+class JUCE_API AudioCDReader : public AudioFormatReader
+{
+public:
+ //==============================================================================
+ /** Returns a list of names of Audio CDs currently available for reading.
+
+ If there's a CD drive but no CD in it, this might return an empty list, or
+ possibly a device that can be opened but which has no tracks, depending
+ on the platform.
+
+ @see createReaderForCD
+ */
+ static StringArray getAvailableCDNames();
+
+ /** Tries to create an AudioFormatReader that can read from an Audio CD.
+
+ @param index the index of one of the available CDs - use getAvailableCDNames()
+ to find out how many there are.
+ @returns a new AudioCDReader object, or nullptr if it couldn't be created. The
+ caller will be responsible for deleting the object returned.
+ */
+ static AudioCDReader* createReaderForCD (const int index);
+
+ //==============================================================================
+ /** Destructor. */
+ ~AudioCDReader();
+
+ /** Implementation of the AudioFormatReader method. */
+ bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
+ int64 startSampleInFile, int numSamples) override;
+
+ /** Checks whether the CD has been removed from the drive. */
+ bool isCDStillPresent() const;
+
+ /** Returns the total number of tracks (audio + data). */
+ int getNumTracks() const;
+
+ /** Finds the sample offset of the start of a track.
+ @param trackNum the track number, where trackNum = 0 is the first track
+ and trackNum = getNumTracks() means the end of the CD.
+ */
+ int getPositionOfTrackStart (int trackNum) const;
+
+ /** Returns true if a given track is an audio track.
+ @param trackNum the track number, where 0 is the first track.
+ */
+ bool isTrackAudio (int trackNum) const;
+
+ /** Returns an array of sample offsets for the start of each track, followed by
+ the sample position of the end of the CD.
+ */
+ const Array& getTrackOffsets() const;
+
+ /** Refreshes the object's table of contents.
+
+ If the disc has been ejected and a different one put in since this
+ object was created, this will cause it to update its idea of how many tracks
+ there are, etc.
+ */
+ void refreshTrackLengths();
+
+ /** Enables scanning for indexes within tracks.
+ @see getLastIndex
+ */
+ void enableIndexScanning (bool enabled);
+
+ /** Returns the index number found during the last read() call.
+
+ Index scanning is turned off by default - turn it on with enableIndexScanning().
+
+ Then when the read() method is called, if it comes across an index within that
+ block, the index number is stored and returned by this method.
+
+ Some devices might not support indexes, of course.
+
+ (If you don't know what CD indexes are, it's unlikely you'll ever need them).
+
+ @see enableIndexScanning
+ */
+ int getLastIndex() const;
+
+ /** Scans a track to find the position of any indexes within it.
+ @param trackNumber the track to look in, where 0 is the first track on the disc
+ @returns an array of sample positions of any index points found (not including
+ the index that marks the start of the track)
+ */
+ Array findIndexesInTrack (const int trackNumber);
+
+ /** Returns the CDDB id number for the CD.
+ It's not a great way of identifying a disc, but it's traditional.
+ */
+ int getCDDBId();
+
+ /** Tries to eject the disk.
+ Ejecting the disk might not actually be possible, e.g. if some other process is using it.
+ */
+ void ejectDisk();
+
+ //==============================================================================
+ enum
+ {
+ framesPerSecond = 75,
+ samplesPerFrame = 44100 / framesPerSecond
+ };
+
+private:
+ //==============================================================================
+ Array trackStartSamples;
+
+ #if JUCE_MAC
+ File volumeDir;
+ Array tracks;
+ int currentReaderTrack;
+ ScopedPointer reader;
+ AudioCDReader (const File& volume);
+
+ #elif JUCE_WINDOWS
+ bool audioTracks [100];
+ void* handle;
+ MemoryBlock buffer;
+ bool indexingEnabled;
+ int lastIndex, firstFrameInBuffer, samplesInBuffer;
+ AudioCDReader (void* handle);
+ int getIndexAt (int samplePos);
+
+ #elif JUCE_LINUX
+ AudioCDReader();
+ #endif
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioCDReader)
+};
+
+#endif
+#endif // JUCE_AUDIOCDREADER_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp
new file mode 100644
index 0000000..fad5eea
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp
@@ -0,0 +1,987 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+AudioDeviceManager::AudioDeviceSetup::AudioDeviceSetup()
+ : sampleRate (0),
+ bufferSize (0),
+ useDefaultInputChannels (true),
+ useDefaultOutputChannels (true)
+{
+}
+
+bool AudioDeviceManager::AudioDeviceSetup::operator== (const AudioDeviceManager::AudioDeviceSetup& other) const
+{
+ return outputDeviceName == other.outputDeviceName
+ && inputDeviceName == other.inputDeviceName
+ && sampleRate == other.sampleRate
+ && bufferSize == other.bufferSize
+ && inputChannels == other.inputChannels
+ && useDefaultInputChannels == other.useDefaultInputChannels
+ && outputChannels == other.outputChannels
+ && useDefaultOutputChannels == other.useDefaultOutputChannels;
+}
+
+//==============================================================================
+class AudioDeviceManager::CallbackHandler : public AudioIODeviceCallback,
+ public MidiInputCallback,
+ public AudioIODeviceType::Listener
+{
+public:
+ CallbackHandler (AudioDeviceManager& adm) noexcept : owner (adm) {}
+
+private:
+ void audioDeviceIOCallback (const float** ins, int numIns, float** outs, int numOuts, int numSamples) override
+ {
+ owner.audioDeviceIOCallbackInt (ins, numIns, outs, numOuts, numSamples);
+ }
+
+ void audioDeviceAboutToStart (AudioIODevice* device) override
+ {
+ owner.audioDeviceAboutToStartInt (device);
+ }
+
+ void audioDeviceStopped() override
+ {
+ owner.audioDeviceStoppedInt();
+ }
+
+ void audioDeviceError (const String& message) override
+ {
+ owner.audioDeviceErrorInt (message);
+ }
+
+ void handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message) override
+ {
+ owner.handleIncomingMidiMessageInt (source, message);
+ }
+
+ void audioDeviceListChanged() override
+ {
+ owner.audioDeviceListChanged();
+ }
+
+ AudioDeviceManager& owner;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackHandler)
+};
+
+
+//==============================================================================
+AudioDeviceManager::AudioDeviceManager()
+ : numInputChansNeeded (0),
+ numOutputChansNeeded (2),
+ listNeedsScanning (true),
+ useInputNames (false),
+ inputLevel (0),
+ testSoundPosition (0),
+ cpuUsageMs (0),
+ timeToCpuScale (0)
+{
+ callbackHandler = new CallbackHandler (*this);
+}
+
+AudioDeviceManager::~AudioDeviceManager()
+{
+ currentAudioDevice = nullptr;
+ defaultMidiOutput = nullptr;
+}
+
+
+//==============================================================================
+void AudioDeviceManager::createDeviceTypesIfNeeded()
+{
+ if (availableDeviceTypes.size() == 0)
+ {
+ OwnedArray types;
+ createAudioDeviceTypes (types);
+
+ for (int i = 0; i < types.size(); ++i)
+ addAudioDeviceType (types.getUnchecked(i));
+
+ types.clear (false);
+
+ if (AudioIODeviceType* first = availableDeviceTypes.getFirst())
+ currentDeviceType = first->getTypeName();
+ }
+}
+
+const OwnedArray& AudioDeviceManager::getAvailableDeviceTypes()
+{
+ scanDevicesIfNeeded();
+ return availableDeviceTypes;
+}
+
+void AudioDeviceManager::audioDeviceListChanged()
+{
+ if (currentAudioDevice != nullptr)
+ {
+ currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate();
+ currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples();
+ currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels();
+ currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels();
+ }
+
+ sendChangeMessage();
+}
+
+//==============================================================================
+static void addIfNotNull (OwnedArray& list, AudioIODeviceType* const device)
+{
+ if (device != nullptr)
+ list.add (device);
+}
+
+void AudioDeviceManager::createAudioDeviceTypes (OwnedArray& list)
+{
+ addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI());
+ addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_DirectSound());
+ addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_ASIO());
+ addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_CoreAudio());
+ addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_iOSAudio());
+ addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_ALSA());
+ addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_JACK());
+ addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_OpenSLES());
+ addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_Android());
+}
+
+void AudioDeviceManager::addAudioDeviceType (AudioIODeviceType* newDeviceType)
+{
+ if (newDeviceType != nullptr)
+ {
+ jassert (lastDeviceTypeConfigs.size() == availableDeviceTypes.size());
+ availableDeviceTypes.add (newDeviceType);
+ lastDeviceTypeConfigs.add (new AudioDeviceSetup());
+
+ newDeviceType->addListener (callbackHandler);
+ }
+}
+
+//==============================================================================
+String AudioDeviceManager::initialise (const int numInputChannelsNeeded,
+ const int numOutputChannelsNeeded,
+ const XmlElement* const xml,
+ const bool selectDefaultDeviceOnFailure,
+ const String& preferredDefaultDeviceName,
+ const AudioDeviceSetup* preferredSetupOptions)
+{
+ scanDevicesIfNeeded();
+
+ numInputChansNeeded = numInputChannelsNeeded;
+ numOutputChansNeeded = numOutputChannelsNeeded;
+
+ if (xml != nullptr && xml->hasTagName ("DEVICESETUP"))
+ return initialiseFromXML (*xml, selectDefaultDeviceOnFailure,
+ preferredDefaultDeviceName, preferredSetupOptions);
+
+ return initialiseDefault (preferredDefaultDeviceName, preferredSetupOptions);
+}
+
+String AudioDeviceManager::initialiseDefault (const String& preferredDefaultDeviceName,
+ const AudioDeviceSetup* preferredSetupOptions)
+{
+ AudioDeviceSetup setup;
+
+ if (preferredSetupOptions != nullptr)
+ {
+ setup = *preferredSetupOptions;
+ }
+ else if (preferredDefaultDeviceName.isNotEmpty())
+ {
+ for (int j = availableDeviceTypes.size(); --j >= 0;)
+ {
+ AudioIODeviceType* const type = availableDeviceTypes.getUnchecked(j);
+
+ const StringArray outs (type->getDeviceNames (false));
+
+ for (int i = 0; i < outs.size(); ++i)
+ {
+ if (outs[i].matchesWildcard (preferredDefaultDeviceName, true))
+ {
+ setup.outputDeviceName = outs[i];
+ break;
+ }
+ }
+
+ const StringArray ins (type->getDeviceNames (true));
+
+ for (int i = 0; i < ins.size(); ++i)
+ {
+ if (ins[i].matchesWildcard (preferredDefaultDeviceName, true))
+ {
+ setup.inputDeviceName = ins[i];
+ break;
+ }
+ }
+ }
+ }
+
+ insertDefaultDeviceNames (setup);
+ return setAudioDeviceSetup (setup, false);
+}
+
+String AudioDeviceManager::initialiseFromXML (const XmlElement& xml,
+ const bool selectDefaultDeviceOnFailure,
+ const String& preferredDefaultDeviceName,
+ const AudioDeviceSetup* preferredSetupOptions)
+{
+ lastExplicitSettings = new XmlElement (xml);
+
+ String error;
+ AudioDeviceSetup setup;
+
+ if (preferredSetupOptions != nullptr)
+ setup = *preferredSetupOptions;
+
+ if (xml.getStringAttribute ("audioDeviceName").isNotEmpty())
+ {
+ setup.inputDeviceName = setup.outputDeviceName
+ = xml.getStringAttribute ("audioDeviceName");
+ }
+ else
+ {
+ setup.inputDeviceName = xml.getStringAttribute ("audioInputDeviceName");
+ setup.outputDeviceName = xml.getStringAttribute ("audioOutputDeviceName");
+ }
+
+ currentDeviceType = xml.getStringAttribute ("deviceType");
+
+ if (findType (currentDeviceType) == nullptr)
+ {
+ if (AudioIODeviceType* const type = findType (setup.inputDeviceName, setup.outputDeviceName))
+ currentDeviceType = type->getTypeName();
+ else if (availableDeviceTypes.size() > 0)
+ currentDeviceType = availableDeviceTypes.getUnchecked(0)->getTypeName();
+ }
+
+ setup.bufferSize = xml.getIntAttribute ("audioDeviceBufferSize");
+ setup.sampleRate = xml.getDoubleAttribute ("audioDeviceRate");
+
+ setup.inputChannels .parseString (xml.getStringAttribute ("audioDeviceInChans", "11"), 2);
+ setup.outputChannels.parseString (xml.getStringAttribute ("audioDeviceOutChans", "11"), 2);
+
+ setup.useDefaultInputChannels = ! xml.hasAttribute ("audioDeviceInChans");
+ setup.useDefaultOutputChannels = ! xml.hasAttribute ("audioDeviceOutChans");
+
+ error = setAudioDeviceSetup (setup, true);
+
+ midiInsFromXml.clear();
+
+ forEachXmlChildElementWithTagName (xml, c, "MIDIINPUT")
+ midiInsFromXml.add (c->getStringAttribute ("name"));
+
+ const StringArray allMidiIns (MidiInput::getDevices());
+
+ for (int i = allMidiIns.size(); --i >= 0;)
+ setMidiInputEnabled (allMidiIns[i], midiInsFromXml.contains (allMidiIns[i]));
+
+ if (error.isNotEmpty() && selectDefaultDeviceOnFailure)
+ error = initialise (numInputChansNeeded, numOutputChansNeeded,
+ nullptr, false, preferredDefaultDeviceName);
+
+ setDefaultMidiOutput (xml.getStringAttribute ("defaultMidiOutput"));
+
+ return error;
+}
+
+String AudioDeviceManager::initialiseWithDefaultDevices (int numInputChannelsNeeded,
+ int numOutputChannelsNeeded)
+{
+ lastExplicitSettings = nullptr;
+
+ return initialise (numInputChannelsNeeded, numOutputChannelsNeeded,
+ nullptr, false, String(), nullptr);
+}
+
+void AudioDeviceManager::insertDefaultDeviceNames (AudioDeviceSetup& setup) const
+{
+ if (AudioIODeviceType* type = getCurrentDeviceTypeObject())
+ {
+ if (setup.outputDeviceName.isEmpty())
+ setup.outputDeviceName = type->getDeviceNames (false) [type->getDefaultDeviceIndex (false)];
+
+ if (setup.inputDeviceName.isEmpty())
+ setup.inputDeviceName = type->getDeviceNames (true) [type->getDefaultDeviceIndex (true)];
+ }
+}
+
+XmlElement* AudioDeviceManager::createStateXml() const
+{
+ return lastExplicitSettings.createCopy();
+}
+
+//==============================================================================
+void AudioDeviceManager::scanDevicesIfNeeded()
+{
+ if (listNeedsScanning)
+ {
+ listNeedsScanning = false;
+
+ createDeviceTypesIfNeeded();
+
+ for (int i = availableDeviceTypes.size(); --i >= 0;)
+ availableDeviceTypes.getUnchecked(i)->scanForDevices();
+ }
+}
+
+AudioIODeviceType* AudioDeviceManager::findType (const String& typeName)
+{
+ scanDevicesIfNeeded();
+
+ for (int i = availableDeviceTypes.size(); --i >= 0;)
+ if (availableDeviceTypes.getUnchecked(i)->getTypeName() == typeName)
+ return availableDeviceTypes.getUnchecked(i);
+
+ return nullptr;
+}
+
+AudioIODeviceType* AudioDeviceManager::findType (const String& inputName, const String& outputName)
+{
+ scanDevicesIfNeeded();
+
+ for (int i = availableDeviceTypes.size(); --i >= 0;)
+ {
+ AudioIODeviceType* const type = availableDeviceTypes.getUnchecked(i);
+
+ if ((inputName.isNotEmpty() && type->getDeviceNames (true).contains (inputName, true))
+ || (outputName.isNotEmpty() && type->getDeviceNames (false).contains (outputName, true)))
+ {
+ return type;
+ }
+ }
+
+ return nullptr;
+}
+
+void AudioDeviceManager::getAudioDeviceSetup (AudioDeviceSetup& setup)
+{
+ setup = currentSetup;
+}
+
+void AudioDeviceManager::deleteCurrentDevice()
+{
+ currentAudioDevice = nullptr;
+ currentSetup.inputDeviceName.clear();
+ currentSetup.outputDeviceName.clear();
+}
+
+void AudioDeviceManager::setCurrentAudioDeviceType (const String& type,
+ const bool treatAsChosenDevice)
+{
+ for (int i = 0; i < availableDeviceTypes.size(); ++i)
+ {
+ if (availableDeviceTypes.getUnchecked(i)->getTypeName() == type
+ && currentDeviceType != type)
+ {
+ if (currentAudioDevice != nullptr)
+ {
+ closeAudioDevice();
+ Thread::sleep (1500); // allow a moment for OS devices to sort themselves out, to help
+ // avoid things like DirectSound/ASIO clashes
+ }
+
+ currentDeviceType = type;
+
+ AudioDeviceSetup s (*lastDeviceTypeConfigs.getUnchecked(i));
+ insertDefaultDeviceNames (s);
+
+ setAudioDeviceSetup (s, treatAsChosenDevice);
+
+ sendChangeMessage();
+ break;
+ }
+ }
+}
+
+AudioIODeviceType* AudioDeviceManager::getCurrentDeviceTypeObject() const
+{
+ for (int i = 0; i < availableDeviceTypes.size(); ++i)
+ if (availableDeviceTypes.getUnchecked(i)->getTypeName() == currentDeviceType)
+ return availableDeviceTypes.getUnchecked(i);
+
+ return availableDeviceTypes[0];
+}
+
+String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup,
+ const bool treatAsChosenDevice)
+{
+ jassert (&newSetup != ¤tSetup); // this will have no effect
+
+ if (newSetup == currentSetup && currentAudioDevice != nullptr)
+ return String();
+
+ if (! (newSetup == currentSetup))
+ sendChangeMessage();
+
+ stopDevice();
+
+ const String newInputDeviceName (numInputChansNeeded == 0 ? String() : newSetup.inputDeviceName);
+ const String newOutputDeviceName (numOutputChansNeeded == 0 ? String() : newSetup.outputDeviceName);
+
+ String error;
+ AudioIODeviceType* type = getCurrentDeviceTypeObject();
+
+ if (type == nullptr || (newInputDeviceName.isEmpty() && newOutputDeviceName.isEmpty()))
+ {
+ deleteCurrentDevice();
+
+ if (treatAsChosenDevice)
+ updateXml();
+
+ return String();
+ }
+
+ if (currentSetup.inputDeviceName != newInputDeviceName
+ || currentSetup.outputDeviceName != newOutputDeviceName
+ || currentAudioDevice == nullptr)
+ {
+ deleteCurrentDevice();
+ scanDevicesIfNeeded();
+
+ if (newOutputDeviceName.isNotEmpty()
+ && ! type->getDeviceNames (false).contains (newOutputDeviceName))
+ {
+ return "No such device: " + newOutputDeviceName;
+ }
+
+ if (newInputDeviceName.isNotEmpty()
+ && ! type->getDeviceNames (true).contains (newInputDeviceName))
+ {
+ return "No such device: " + newInputDeviceName;
+ }
+
+ currentAudioDevice = type->createDevice (newOutputDeviceName, newInputDeviceName);
+
+ if (currentAudioDevice == nullptr)
+ error = "Can't open the audio device!\n\n"
+ "This may be because another application is currently using the same device - "
+ "if so, you should close any other applications and try again!";
+ else
+ error = currentAudioDevice->getLastError();
+
+ if (error.isNotEmpty())
+ {
+ deleteCurrentDevice();
+ return error;
+ }
+
+ if (newSetup.useDefaultInputChannels)
+ {
+ inputChannels.clear();
+ inputChannels.setRange (0, numInputChansNeeded, true);
+ }
+
+ if (newSetup.useDefaultOutputChannels)
+ {
+ outputChannels.clear();
+ outputChannels.setRange (0, numOutputChansNeeded, true);
+ }
+
+ if (newInputDeviceName.isEmpty()) inputChannels.clear();
+ if (newOutputDeviceName.isEmpty()) outputChannels.clear();
+ }
+
+ if (! newSetup.useDefaultInputChannels) inputChannels = newSetup.inputChannels;
+ if (! newSetup.useDefaultOutputChannels) outputChannels = newSetup.outputChannels;
+
+ currentSetup = newSetup;
+
+ currentSetup.sampleRate = chooseBestSampleRate (newSetup.sampleRate);
+ currentSetup.bufferSize = chooseBestBufferSize (newSetup.bufferSize);
+
+ error = currentAudioDevice->open (inputChannels,
+ outputChannels,
+ currentSetup.sampleRate,
+ currentSetup.bufferSize);
+
+ if (error.isEmpty())
+ {
+ currentDeviceType = currentAudioDevice->getTypeName();
+
+ currentAudioDevice->start (callbackHandler);
+
+ currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate();
+ currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples();
+ currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels();
+ currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels();
+
+ for (int i = 0; i < availableDeviceTypes.size(); ++i)
+ if (availableDeviceTypes.getUnchecked (i)->getTypeName() == currentDeviceType)
+ *(lastDeviceTypeConfigs.getUnchecked (i)) = currentSetup;
+
+ if (treatAsChosenDevice)
+ updateXml();
+ }
+ else
+ {
+ deleteCurrentDevice();
+ }
+
+ return error;
+}
+
+double AudioDeviceManager::chooseBestSampleRate (double rate) const
+{
+ jassert (currentAudioDevice != nullptr);
+
+ const Array rates (currentAudioDevice->getAvailableSampleRates());
+
+ if (rate > 0 && rates.contains (rate))
+ return rate;
+
+ double lowestAbove44 = 0.0;
+
+ for (int i = rates.size(); --i >= 0;)
+ {
+ const double sr = rates[i];
+
+ if (sr >= 44100.0 && (lowestAbove44 < 1.0 || sr < lowestAbove44))
+ lowestAbove44 = sr;
+ }
+
+ if (lowestAbove44 > 0.0)
+ return lowestAbove44;
+
+ return rates[0];
+}
+
+int AudioDeviceManager::chooseBestBufferSize (int bufferSize) const
+{
+ jassert (currentAudioDevice != nullptr);
+
+ if (bufferSize > 0 && currentAudioDevice->getAvailableBufferSizes().contains (bufferSize))
+ return bufferSize;
+
+ return currentAudioDevice->getDefaultBufferSize();
+}
+
+void AudioDeviceManager::stopDevice()
+{
+ if (currentAudioDevice != nullptr)
+ currentAudioDevice->stop();
+
+ testSound = nullptr;
+}
+
+void AudioDeviceManager::closeAudioDevice()
+{
+ stopDevice();
+ currentAudioDevice = nullptr;
+}
+
+void AudioDeviceManager::restartLastAudioDevice()
+{
+ if (currentAudioDevice == nullptr)
+ {
+ if (currentSetup.inputDeviceName.isEmpty()
+ && currentSetup.outputDeviceName.isEmpty())
+ {
+ // This method will only reload the last device that was running
+ // before closeAudioDevice() was called - you need to actually open
+ // one first, with setAudioDevice().
+ jassertfalse;
+ return;
+ }
+
+ AudioDeviceSetup s (currentSetup);
+ setAudioDeviceSetup (s, false);
+ }
+}
+
+void AudioDeviceManager::updateXml()
+{
+ lastExplicitSettings = new XmlElement ("DEVICESETUP");
+
+ lastExplicitSettings->setAttribute ("deviceType", currentDeviceType);
+ lastExplicitSettings->setAttribute ("audioOutputDeviceName", currentSetup.outputDeviceName);
+ lastExplicitSettings->setAttribute ("audioInputDeviceName", currentSetup.inputDeviceName);
+
+ if (currentAudioDevice != nullptr)
+ {
+ lastExplicitSettings->setAttribute ("audioDeviceRate", currentAudioDevice->getCurrentSampleRate());
+
+ if (currentAudioDevice->getDefaultBufferSize() != currentAudioDevice->getCurrentBufferSizeSamples())
+ lastExplicitSettings->setAttribute ("audioDeviceBufferSize", currentAudioDevice->getCurrentBufferSizeSamples());
+
+ if (! currentSetup.useDefaultInputChannels)
+ lastExplicitSettings->setAttribute ("audioDeviceInChans", currentSetup.inputChannels.toString (2));
+
+ if (! currentSetup.useDefaultOutputChannels)
+ lastExplicitSettings->setAttribute ("audioDeviceOutChans", currentSetup.outputChannels.toString (2));
+ }
+
+ for (int i = 0; i < enabledMidiInputs.size(); ++i)
+ lastExplicitSettings->createNewChildElement ("MIDIINPUT")
+ ->setAttribute ("name", enabledMidiInputs[i]->getName());
+
+ if (midiInsFromXml.size() > 0)
+ {
+ // Add any midi devices that have been enabled before, but which aren't currently
+ // open because the device has been disconnected.
+ const StringArray availableMidiDevices (MidiInput::getDevices());
+
+ for (int i = 0; i < midiInsFromXml.size(); ++i)
+ if (! availableMidiDevices.contains (midiInsFromXml[i], true))
+ lastExplicitSettings->createNewChildElement ("MIDIINPUT")
+ ->setAttribute ("name", midiInsFromXml[i]);
+ }
+
+ if (defaultMidiOutputName.isNotEmpty())
+ lastExplicitSettings->setAttribute ("defaultMidiOutput", defaultMidiOutputName);
+}
+
+//==============================================================================
+void AudioDeviceManager::addAudioCallback (AudioIODeviceCallback* newCallback)
+{
+ {
+ const ScopedLock sl (audioCallbackLock);
+ if (callbacks.contains (newCallback))
+ return;
+ }
+
+ if (currentAudioDevice != nullptr && newCallback != nullptr)
+ newCallback->audioDeviceAboutToStart (currentAudioDevice);
+
+ const ScopedLock sl (audioCallbackLock);
+ callbacks.add (newCallback);
+}
+
+void AudioDeviceManager::removeAudioCallback (AudioIODeviceCallback* callbackToRemove)
+{
+ if (callbackToRemove != nullptr)
+ {
+ bool needsDeinitialising = currentAudioDevice != nullptr;
+
+ {
+ const ScopedLock sl (audioCallbackLock);
+
+ needsDeinitialising = needsDeinitialising && callbacks.contains (callbackToRemove);
+ callbacks.removeFirstMatchingValue (callbackToRemove);
+ }
+
+ if (needsDeinitialising)
+ callbackToRemove->audioDeviceStopped();
+ }
+}
+
+void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelData,
+ int numInputChannels,
+ float** outputChannelData,
+ int numOutputChannels,
+ int numSamples)
+{
+ const ScopedLock sl (audioCallbackLock);
+
+ if (inputLevelMeasurementEnabledCount.get() > 0 && numInputChannels > 0)
+ {
+ for (int j = 0; j < numSamples; ++j)
+ {
+ float s = 0;
+
+ for (int i = 0; i < numInputChannels; ++i)
+ s += std::abs (inputChannelData[i][j]);
+
+ s /= numInputChannels;
+
+ const double decayFactor = 0.99992;
+
+ if (s > inputLevel)
+ inputLevel = s;
+ else if (inputLevel > 0.001f)
+ inputLevel *= decayFactor;
+ else
+ inputLevel = 0;
+ }
+ }
+ else
+ {
+ inputLevel = 0;
+ }
+
+ if (callbacks.size() > 0)
+ {
+ const double callbackStartTime = Time::getMillisecondCounterHiRes();
+
+ tempBuffer.setSize (jmax (1, numOutputChannels), jmax (1, numSamples), false, false, true);
+
+ callbacks.getUnchecked(0)->audioDeviceIOCallback (inputChannelData, numInputChannels,
+ outputChannelData, numOutputChannels, numSamples);
+
+ float** const tempChans = tempBuffer.getArrayOfWritePointers();
+
+ for (int i = callbacks.size(); --i > 0;)
+ {
+ callbacks.getUnchecked(i)->audioDeviceIOCallback (inputChannelData, numInputChannels,
+ tempChans, numOutputChannels, numSamples);
+
+ for (int chan = 0; chan < numOutputChannels; ++chan)
+ {
+ if (const float* const src = tempChans [chan])
+ if (float* const dst = outputChannelData [chan])
+ for (int j = 0; j < numSamples; ++j)
+ dst[j] += src[j];
+ }
+ }
+
+ const double msTaken = Time::getMillisecondCounterHiRes() - callbackStartTime;
+ const double filterAmount = 0.2;
+ cpuUsageMs += filterAmount * (msTaken - cpuUsageMs);
+ }
+ else
+ {
+ for (int i = 0; i < numOutputChannels; ++i)
+ zeromem (outputChannelData[i], sizeof (float) * (size_t) numSamples);
+ }
+
+ if (testSound != nullptr)
+ {
+ const int numSamps = jmin (numSamples, testSound->getNumSamples() - testSoundPosition);
+ const float* const src = testSound->getReadPointer (0, testSoundPosition);
+
+ for (int i = 0; i < numOutputChannels; ++i)
+ for (int j = 0; j < numSamps; ++j)
+ outputChannelData [i][j] += src[j];
+
+ testSoundPosition += numSamps;
+ if (testSoundPosition >= testSound->getNumSamples())
+ testSound = nullptr;
+ }
+}
+
+void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device)
+{
+ cpuUsageMs = 0;
+
+ const double sampleRate = device->getCurrentSampleRate();
+ const int blockSize = device->getCurrentBufferSizeSamples();
+
+ if (sampleRate > 0.0 && blockSize > 0)
+ {
+ const double msPerBlock = 1000.0 * blockSize / sampleRate;
+ timeToCpuScale = (msPerBlock > 0.0) ? (1.0 / msPerBlock) : 0.0;
+ }
+
+ {
+ const ScopedLock sl (audioCallbackLock);
+ for (int i = callbacks.size(); --i >= 0;)
+ callbacks.getUnchecked(i)->audioDeviceAboutToStart (device);
+ }
+
+ sendChangeMessage();
+}
+
+void AudioDeviceManager::audioDeviceStoppedInt()
+{
+ cpuUsageMs = 0;
+ timeToCpuScale = 0;
+ sendChangeMessage();
+
+ const ScopedLock sl (audioCallbackLock);
+ for (int i = callbacks.size(); --i >= 0;)
+ callbacks.getUnchecked(i)->audioDeviceStopped();
+}
+
+void AudioDeviceManager::audioDeviceErrorInt (const String& message)
+{
+ const ScopedLock sl (audioCallbackLock);
+ for (int i = callbacks.size(); --i >= 0;)
+ callbacks.getUnchecked(i)->audioDeviceError (message);
+}
+
+double AudioDeviceManager::getCpuUsage() const
+{
+ return jlimit (0.0, 1.0, timeToCpuScale * cpuUsageMs);
+}
+
+//==============================================================================
+void AudioDeviceManager::setMidiInputEnabled (const String& name, const bool enabled)
+{
+ if (enabled != isMidiInputEnabled (name))
+ {
+ if (enabled)
+ {
+ const int index = MidiInput::getDevices().indexOf (name);
+
+ if (index >= 0)
+ {
+ if (MidiInput* const midiIn = MidiInput::openDevice (index, callbackHandler))
+ {
+ enabledMidiInputs.add (midiIn);
+ midiIn->start();
+ }
+ }
+ }
+ else
+ {
+ for (int i = enabledMidiInputs.size(); --i >= 0;)
+ if (enabledMidiInputs[i]->getName() == name)
+ enabledMidiInputs.remove (i);
+ }
+
+ updateXml();
+ sendChangeMessage();
+ }
+}
+
+bool AudioDeviceManager::isMidiInputEnabled (const String& name) const
+{
+ for (int i = enabledMidiInputs.size(); --i >= 0;)
+ if (enabledMidiInputs[i]->getName() == name)
+ return true;
+
+ return false;
+}
+
+void AudioDeviceManager::addMidiInputCallback (const String& name, MidiInputCallback* callbackToAdd)
+{
+ removeMidiInputCallback (name, callbackToAdd);
+
+ if (name.isEmpty() || isMidiInputEnabled (name))
+ {
+ const ScopedLock sl (midiCallbackLock);
+
+ MidiCallbackInfo mc;
+ mc.deviceName = name;
+ mc.callback = callbackToAdd;
+ midiCallbacks.add (mc);
+ }
+}
+
+void AudioDeviceManager::removeMidiInputCallback (const String& name, MidiInputCallback* callbackToRemove)
+{
+ for (int i = midiCallbacks.size(); --i >= 0;)
+ {
+ const MidiCallbackInfo& mc = midiCallbacks.getReference(i);
+
+ if (mc.callback == callbackToRemove && mc.deviceName == name)
+ {
+ const ScopedLock sl (midiCallbackLock);
+ midiCallbacks.remove (i);
+ }
+ }
+}
+
+void AudioDeviceManager::handleIncomingMidiMessageInt (MidiInput* source, const MidiMessage& message)
+{
+ if (! message.isActiveSense())
+ {
+ const ScopedLock sl (midiCallbackLock);
+
+ for (int i = 0; i < midiCallbacks.size(); ++i)
+ {
+ const MidiCallbackInfo& mc = midiCallbacks.getReference(i);
+
+ if (mc.deviceName.isEmpty() || mc.deviceName == source->getName())
+ mc.callback->handleIncomingMidiMessage (source, message);
+ }
+ }
+}
+
+//==============================================================================
+void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName)
+{
+ if (defaultMidiOutputName != deviceName)
+ {
+ Array oldCallbacks;
+
+ {
+ const ScopedLock sl (audioCallbackLock);
+ oldCallbacks.swapWith (callbacks);
+ }
+
+ if (currentAudioDevice != nullptr)
+ for (int i = oldCallbacks.size(); --i >= 0;)
+ oldCallbacks.getUnchecked(i)->audioDeviceStopped();
+
+ defaultMidiOutput = nullptr;
+ defaultMidiOutputName = deviceName;
+
+ if (deviceName.isNotEmpty())
+ defaultMidiOutput = MidiOutput::openDevice (MidiOutput::getDevices().indexOf (deviceName));
+
+ if (currentAudioDevice != nullptr)
+ for (int i = oldCallbacks.size(); --i >= 0;)
+ oldCallbacks.getUnchecked(i)->audioDeviceAboutToStart (currentAudioDevice);
+
+ {
+ const ScopedLock sl (audioCallbackLock);
+ oldCallbacks.swapWith (callbacks);
+ }
+
+ updateXml();
+ sendChangeMessage();
+ }
+}
+
+//==============================================================================
+void AudioDeviceManager::playTestSound()
+{
+ { // cunningly nested to swap, unlock and delete in that order.
+ ScopedPointer oldSound;
+
+ {
+ const ScopedLock sl (audioCallbackLock);
+ oldSound = testSound;
+ }
+ }
+
+ testSoundPosition = 0;
+
+ if (currentAudioDevice != nullptr)
+ {
+ const double sampleRate = currentAudioDevice->getCurrentSampleRate();
+ const int soundLength = (int) sampleRate;
+
+ const double frequency = 440.0;
+ const float amplitude = 0.5f;
+
+ const double phasePerSample = double_Pi * 2.0 / (sampleRate / frequency);
+
+ AudioSampleBuffer* const newSound = new AudioSampleBuffer (1, soundLength);
+
+ for (int i = 0; i < soundLength; ++i)
+ newSound->setSample (0, i, amplitude * (float) std::sin (i * phasePerSample));
+
+ newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f);
+ newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f);
+
+ const ScopedLock sl (audioCallbackLock);
+ testSound = newSound;
+ }
+}
+
+void AudioDeviceManager::enableInputLevelMeasurement (const bool enableMeasurement)
+{
+ if (enableMeasurement)
+ ++inputLevelMeasurementEnabledCount;
+ else
+ --inputLevelMeasurementEnabledCount;
+
+ inputLevel = 0;
+}
+
+double AudioDeviceManager::getCurrentInputLevel() const
+{
+ jassert (inputLevelMeasurementEnabledCount.get() > 0); // you need to call enableInputLevelMeasurement() before using this!
+ return inputLevel;
+}
diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h
new file mode 100644
index 0000000..f1e483f
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h
@@ -0,0 +1,512 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_AUDIODEVICEMANAGER_H_INCLUDED
+#define JUCE_AUDIODEVICEMANAGER_H_INCLUDED
+
+
+//==============================================================================
+/**
+ Manages the state of some audio and midi i/o devices.
+
+ This class keeps tracks of a currently-selected audio device, through
+ with which it continuously streams data from an audio callback, as well as
+ one or more midi inputs.
+
+ The idea is that your application will create one global instance of this object,
+ and let it take care of creating and deleting specific types of audio devices
+ internally. So when the device is changed, your callbacks will just keep running
+ without having to worry about this.
+
+ The manager can save and reload all of its device settings as XML, which
+ makes it very easy for you to save and reload the audio setup of your
+ application.
+
+ And to make it easy to let the user change its settings, there's a component
+ to do just that - the AudioDeviceSelectorComponent class, which contains a set of
+ device selection/sample-rate/latency controls.
+
+ To use an AudioDeviceManager, create one, and use initialise() to set it up. Then
+ call addAudioCallback() to register your audio callback with it, and use that to process
+ your audio data.
+
+ The manager also acts as a handy hub for incoming midi messages, allowing a
+ listener to register for messages from either a specific midi device, or from whatever
+ the current default midi input device is. The listener then doesn't have to worry about
+ re-registering with different midi devices if they are changed or deleted.
+
+ And yet another neat trick is that amount of CPU time being used is measured and
+ available with the getCpuUsage() method.
+
+ The AudioDeviceManager is a ChangeBroadcaster, and will send a change message to
+ listeners whenever one of its settings is changed.
+
+ @see AudioDeviceSelectorComponent, AudioIODevice, AudioIODeviceType
+*/
+class JUCE_API AudioDeviceManager : public ChangeBroadcaster
+{
+public:
+ //==============================================================================
+ /** Creates a default AudioDeviceManager.
+
+ Initially no audio device will be selected. You should call the initialise() method
+ and register an audio callback with setAudioCallback() before it'll be able to
+ actually make any noise.
+ */
+ AudioDeviceManager();
+
+ /** Destructor. */
+ ~AudioDeviceManager();
+
+ //==============================================================================
+ /**
+ This structure holds a set of properties describing the current audio setup.
+
+ An AudioDeviceManager uses this class to save/load its current settings, and to
+ specify your preferred options when opening a device.
+
+ @see AudioDeviceManager::setAudioDeviceSetup(), AudioDeviceManager::initialise()
+ */
+ struct JUCE_API AudioDeviceSetup
+ {
+ /** Creates an AudioDeviceSetup object.
+
+ The default constructor sets all the member variables to indicate default values.
+ You can then fill-in any values you want to before passing the object to
+ AudioDeviceManager::initialise().
+ */
+ AudioDeviceSetup();
+
+ bool operator== (const AudioDeviceSetup& other) const;
+
+ /** The name of the audio device used for output.
+ The name has to be one of the ones listed by the AudioDeviceManager's currently
+ selected device type.
+ This may be the same as the input device.
+ An empty string indicates the default device.
+ */
+ String outputDeviceName;
+
+ /** The name of the audio device used for input.
+ This may be the same as the output device.
+ An empty string indicates the default device.
+ */
+ String inputDeviceName;
+
+ /** The current sample rate.
+ This rate is used for both the input and output devices.
+ A value of 0 indicates that you don't care what rate is used, and the
+ device will choose a sensible rate for you.
+ */
+ double sampleRate;
+
+ /** The buffer size, in samples.
+ This buffer size is used for both the input and output devices.
+ A value of 0 indicates the default buffer size.
+ */
+ int bufferSize;
+
+ /** The set of active input channels.
+ The bits that are set in this array indicate the channels of the
+ input device that are active.
+ If useDefaultInputChannels is true, this value is ignored.
+ */
+ BigInteger inputChannels;
+
+ /** If this is true, it indicates that the inputChannels array
+ should be ignored, and instead, the device's default channels
+ should be used.
+ */
+ bool useDefaultInputChannels;
+
+ /** The set of active output channels.
+ The bits that are set in this array indicate the channels of the
+ input device that are active.
+ If useDefaultOutputChannels is true, this value is ignored.
+ */
+ BigInteger outputChannels;
+
+ /** If this is true, it indicates that the outputChannels array
+ should be ignored, and instead, the device's default channels
+ should be used.
+ */
+ bool useDefaultOutputChannels;
+ };
+
+
+ //==============================================================================
+ /** Opens a set of audio devices ready for use.
+
+ This will attempt to open either a default audio device, or one that was
+ previously saved as XML.
+
+ @param numInputChannelsNeeded the maximum number of input channels your app would like to
+ use (the actual number of channels opened may be less than
+ the number requested)
+ @param numOutputChannelsNeeded the maximum number of output channels your app would like to
+ use (the actual number of channels opened may be less than
+ the number requested)
+ @param savedState either a previously-saved state that was produced
+ by createStateXml(), or nullptr if you want the manager
+ to choose the best device to open.
+ @param selectDefaultDeviceOnFailure if true, then if the device specified in the XML
+ fails to open, then a default device will be used
+ instead. If false, then on failure, no device is
+ opened.
+ @param preferredDefaultDeviceName if this is not empty, and there's a device with this
+ name, then that will be used as the default device
+ (assuming that there wasn't one specified in the XML).
+ The string can actually be a simple wildcard, containing "*"
+ and "?" characters
+ @param preferredSetupOptions if this is non-null, the structure will be used as the
+ set of preferred settings when opening the device. If you
+ use this parameter, the preferredDefaultDeviceName
+ field will be ignored
+
+ @returns an error message if anything went wrong, or an empty string if it worked ok.
+ */
+ String initialise (int numInputChannelsNeeded,
+ int numOutputChannelsNeeded,
+ const XmlElement* savedState,
+ bool selectDefaultDeviceOnFailure,
+ const String& preferredDefaultDeviceName = String(),
+ const AudioDeviceSetup* preferredSetupOptions = nullptr);
+
+ /** Resets everything to a default device setup, clearing any stored settings. */
+ String initialiseWithDefaultDevices (int numInputChannelsNeeded,
+ int numOutputChannelsNeeded);
+
+ /** Returns some XML representing the current state of the manager.
+
+ This stores the current device, its samplerate, block size, etc, and
+ can be restored later with initialise().
+
+ Note that this can return a null pointer if no settings have been explicitly changed
+ (i.e. if the device manager has just been left in its default state).
+ */
+ XmlElement* createStateXml() const;
+
+ //==============================================================================
+ /** Returns the current device properties that are in use.
+ @see setAudioDeviceSetup
+ */
+ void getAudioDeviceSetup (AudioDeviceSetup& result);
+
+ /** Changes the current device or its settings.
+
+ If you want to change a device property, like the current sample rate or
+ block size, you can call getAudioDeviceSetup() to retrieve the current
+ settings, then tweak the appropriate fields in the AudioDeviceSetup structure,
+ and pass it back into this method to apply the new settings.
+
+ @param newSetup the settings that you'd like to use
+ @param treatAsChosenDevice if this is true and if the device opens correctly, these new
+ settings will be taken as having been explicitly chosen by the
+ user, and the next time createStateXml() is called, these settings
+ will be returned. If it's false, then the device is treated as a
+ temporary or default device, and a call to createStateXml() will
+ return either the last settings that were made with treatAsChosenDevice
+ as true, or the last XML settings that were passed into initialise().
+ @returns an error message if anything went wrong, or an empty string if it worked ok.
+
+ @see getAudioDeviceSetup
+ */
+ String setAudioDeviceSetup (const AudioDeviceSetup& newSetup,
+ bool treatAsChosenDevice);
+
+
+ /** Returns the currently-active audio device. */
+ AudioIODevice* getCurrentAudioDevice() const noexcept { return currentAudioDevice; }
+
+ /** Returns the type of audio device currently in use.
+ @see setCurrentAudioDeviceType
+ */
+ String getCurrentAudioDeviceType() const { return currentDeviceType; }
+
+ /** Returns the currently active audio device type object.
+ Don't keep a copy of this pointer - it's owned by the device manager and could
+ change at any time.
+ */
+ AudioIODeviceType* getCurrentDeviceTypeObject() const;
+
+ /** Changes the class of audio device being used.
+
+ This switches between, e.g. ASIO and DirectSound. On the Mac you probably won't ever call
+ this because there's only one type: CoreAudio.
+
+ For a list of types, see getAvailableDeviceTypes().
+ */
+ void setCurrentAudioDeviceType (const String& type,
+ bool treatAsChosenDevice);
+
+ /** Closes the currently-open device.
+ You can call restartLastAudioDevice() later to reopen it in the same state
+ that it was just in.
+ */
+ void closeAudioDevice();
+
+ /** Tries to reload the last audio device that was running.
+
+ Note that this only reloads the last device that was running before
+ closeAudioDevice() was called - it doesn't reload any kind of saved-state,
+ and can only be called after a device has been opened with SetAudioDevice().
+
+ If a device is already open, this call will do nothing.
+ */
+ void restartLastAudioDevice();
+
+ //==============================================================================
+ /** Registers an audio callback to be used.
+
+ The manager will redirect callbacks from whatever audio device is currently
+ in use to all registered callback objects. If more than one callback is
+ active, they will all be given the same input data, and their outputs will
+ be summed.
+
+ If necessary, this method will invoke audioDeviceAboutToStart() on the callback
+ object before returning.
+
+ To remove a callback, use removeAudioCallback().
+ */
+ void addAudioCallback (AudioIODeviceCallback* newCallback);
+
+ /** Deregisters a previously added callback.
+
+ If necessary, this method will invoke audioDeviceStopped() on the callback
+ object before returning.
+
+ @see addAudioCallback
+ */
+ void removeAudioCallback (AudioIODeviceCallback* callback);
+
+ //==============================================================================
+ /** 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
+ time spent in the callbacks.
+ */
+ double getCpuUsage() const;
+
+ //==============================================================================
+ /** Enables or disables a midi input device.
+
+ The list of devices can be obtained with the MidiInput::getDevices() method.
+
+ Any incoming messages from enabled input devices will be forwarded on to all the
+ listeners that have been registered with the addMidiInputCallback() method. They
+ can either register for messages from a particular device, or from just the
+ "default" midi input.
+
+ Routing the midi input via an AudioDeviceManager means that when a listener
+ registers for the default midi input, this default device can be changed by the
+ manager without the listeners having to know about it or re-register.
+
+ It also means that a listener can stay registered for a midi input that is disabled
+ or not present, so that when the input is re-enabled, the listener will start
+ receiving messages again.
+
+ @see addMidiInputCallback, isMidiInputEnabled
+ */
+ void setMidiInputEnabled (const String& midiInputDeviceName, bool enabled);
+
+ /** Returns true if a given midi input device is being used.
+ @see setMidiInputEnabled
+ */
+ bool isMidiInputEnabled (const String& midiInputDeviceName) const;
+
+ /** Registers a listener for callbacks when midi events arrive from a midi input.
+
+ The device name can be empty to indicate that it wants to receive all incoming
+ events from all the enabled MIDI inputs. Or it can be the name of one of the
+ 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
+ events forwarded on to listeners.
+ */
+ void addMidiInputCallback (const String& midiInputDeviceName,
+ MidiInputCallback* callback);
+
+ /** Removes a listener that was previously registered with addMidiInputCallback(). */
+ void removeMidiInputCallback (const String& midiInputDeviceName,
+ MidiInputCallback* callback);
+
+ //==============================================================================
+ /** Sets a midi output device to use as the default.
+
+ The list of devices can be obtained with the MidiOutput::getDevices() method.
+
+ The specified device will be opened automatically and can be retrieved with the
+ getDefaultMidiOutput() method.
+
+ Pass in an empty string to deselect all devices. For the default device, you
+ can use MidiOutput::getDevices() [MidiOutput::getDefaultDeviceIndex()].
+
+ @see getDefaultMidiOutput, getDefaultMidiOutputName
+ */
+ void setDefaultMidiOutput (const String& deviceName);
+
+ /** Returns the name of the default midi output.
+ @see setDefaultMidiOutput, getDefaultMidiOutput
+ */
+ const String& getDefaultMidiOutputName() const noexcept { return defaultMidiOutputName; }
+
+ /** Returns the current default midi output device.
+ If no device has been selected, or the device can't be opened, this will return nullptr.
+ @see getDefaultMidiOutputName
+ */
+ MidiOutput* getDefaultMidiOutput() const noexcept { return defaultMidiOutput; }
+
+ /** Returns a list of the types of device supported. */
+ const OwnedArray& getAvailableDeviceTypes();
+
+ //==============================================================================
+ /** Creates a list of available types.
+
+ This will add a set of new AudioIODeviceType objects to the specified list, to
+ represent each available types of device.
+
+ You can override this if your app needs to do something specific, like avoid
+ using DirectSound devices, etc.
+ */
+ virtual void createAudioDeviceTypes (OwnedArray& types);
+
+ /** Adds a new device type to the list of types.
+ The manager will take ownership of the object that is passed-in.
+ */
+ void addAudioDeviceType (AudioIODeviceType* newDeviceType);
+
+ //==============================================================================
+ /** Plays a beep through the current audio device.
+
+ This is here to allow the audio setup UI panels to easily include a "test"
+ button so that the user can check where the audio is coming from.
+ */
+ void playTestSound();
+
+ /** Turns on level-measuring.
+
+ When enabled, the device manager will measure the peak input level
+ across all channels, and you can get this level by calling getCurrentInputLevel().
+
+ This is mainly intended for audio setup UI panels to use to create a mic
+ level display, so that the user can check that they've selected the right
+ device.
+
+ A simple filter is used to make the level decay smoothly, but this is
+ only intended for giving rough feedback, and not for any kind of accurate
+ measurement.
+ */
+ void enableInputLevelMeasurement (bool enableMeasurement);
+
+ /** Returns the current input level.
+ To use this, you must first enable it by calling enableInputLevelMeasurement().
+ See enableInputLevelMeasurement() for more info.
+ */
+ double getCurrentInputLevel() const;
+
+ /** Returns the a lock that can be used to synchronise access to the audio callback.
+ Obviously while this is locked, you're blocking the audio thread from running, so
+ it must only be used for very brief periods when absolutely necessary.
+ */
+ CriticalSection& getAudioCallbackLock() noexcept { return audioCallbackLock; }
+
+ /** Returns the a lock that can be used to synchronise access to the midi callback.
+ Obviously while this is locked, you're blocking the midi system from running, so
+ it must only be used for very brief periods when absolutely necessary.
+ */
+ CriticalSection& getMidiCallbackLock() noexcept { return midiCallbackLock; }
+
+private:
+ //==============================================================================
+ OwnedArray availableDeviceTypes;
+ OwnedArray lastDeviceTypeConfigs;
+
+ AudioDeviceSetup currentSetup;
+ ScopedPointer currentAudioDevice;
+ Array callbacks;
+ int numInputChansNeeded, numOutputChansNeeded;
+ String currentDeviceType;
+ BigInteger inputChannels, outputChannels;
+ ScopedPointer lastExplicitSettings;
+ mutable bool listNeedsScanning;
+ bool useInputNames;
+ Atomic inputLevelMeasurementEnabledCount;
+ double inputLevel;
+ ScopedPointer testSound;
+ int testSoundPosition;
+ AudioSampleBuffer tempBuffer;
+
+ struct MidiCallbackInfo
+ {
+ String deviceName;
+ MidiInputCallback* callback;
+ };
+
+ StringArray midiInsFromXml;
+ OwnedArray enabledMidiInputs;
+ Array midiCallbacks;
+
+ String defaultMidiOutputName;
+ ScopedPointer defaultMidiOutput;
+ CriticalSection audioCallbackLock, midiCallbackLock;
+
+ double cpuUsageMs, timeToCpuScale;
+
+ //==============================================================================
+ class CallbackHandler;
+ friend class CallbackHandler;
+ friend struct ContainerDeletePolicy;
+ ScopedPointer callbackHandler;
+
+ void audioDeviceIOCallbackInt (const float** inputChannelData, int totalNumInputChannels,
+ float** outputChannelData, int totalNumOutputChannels, int numSamples);
+ void audioDeviceAboutToStartInt (AudioIODevice*);
+ void audioDeviceStoppedInt();
+ void audioDeviceErrorInt (const String&);
+ void handleIncomingMidiMessageInt (MidiInput*, const MidiMessage&);
+ void audioDeviceListChanged();
+
+ String restartDevice (int blockSizeToUse, double sampleRateToUse,
+ const BigInteger& ins, const BigInteger& outs);
+ void stopDevice();
+
+ void updateXml();
+
+ void createDeviceTypesIfNeeded();
+ void scanDevicesIfNeeded();
+ void deleteCurrentDevice();
+ double chooseBestSampleRate (double preferred) const;
+ int chooseBestBufferSize (int preferred) const;
+ void insertDefaultDeviceNames (AudioDeviceSetup&) const;
+ String initialiseDefault (const String& preferredDefaultDeviceName, const AudioDeviceSetup*);
+ String initialiseFromXML (const XmlElement&, bool selectDefaultDeviceOnFailure,
+ const String& preferredDefaultDeviceName, const AudioDeviceSetup*);
+
+ AudioIODeviceType* findType (const String& inputName, const String& outputName);
+ AudioIODeviceType* findType (const String& typeName);
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioDeviceManager)
+};
+
+#endif // JUCE_AUDIODEVICEMANAGER_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.cpp b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.cpp
new file mode 100644
index 0000000..95221ba
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.cpp
@@ -0,0 +1,41 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+AudioIODevice::AudioIODevice (const String& deviceName, const String& deviceTypeName)
+ : name (deviceName), typeName (deviceTypeName)
+{
+}
+
+AudioIODevice::~AudioIODevice() {}
+
+void AudioIODeviceCallback::audioDeviceError (const String&) {}
+bool AudioIODevice::setAudioPreprocessingEnabled (bool) { return false; }
+bool AudioIODevice::hasControlPanel() const { return false; }
+
+bool AudioIODevice::showControlPanel()
+{
+ jassertfalse; // this should only be called for devices which return true from
+ // their hasControlPanel() method.
+ return false;
+}
diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h
new file mode 100644
index 0000000..8ce41d0
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h
@@ -0,0 +1,309 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_AUDIOIODEVICE_H_INCLUDED
+#define JUCE_AUDIOIODEVICE_H_INCLUDED
+
+class AudioIODevice;
+
+
+//==============================================================================
+/**
+ One of these is passed to an AudioIODevice object to stream the audio data
+ in and out.
+
+ The AudioIODevice will repeatedly call this class's audioDeviceIOCallback()
+ method on its own high-priority audio thread, when it needs to send or receive
+ the next block of data.
+
+ @see AudioIODevice, AudioDeviceManager
+*/
+class JUCE_API AudioIODeviceCallback
+{
+public:
+ /** Destructor. */
+ virtual ~AudioIODeviceCallback() {}
+
+ /** Processes a block of incoming and outgoing audio data.
+
+ The subclass's implementation should use the incoming audio for whatever
+ purposes it needs to, and must fill all the output channels with the next
+ block of output data before returning.
+
+ The channel data is arranged with the same array indices as the channel name
+ array returned by AudioIODevice::getOutputChannelNames(), but those channels
+ that aren't specified in AudioIODevice::open() will have a null pointer for their
+ associated channel, so remember to check for this.
+
+ @param inputChannelData a set of arrays containing the audio data for each
+ incoming channel - this data is valid until the function
+ returns. There will be one channel of data for each input
+ channel that was enabled when the audio device was opened
+ (see AudioIODevice::open())
+ @param numInputChannels the number of pointers to channel data in the
+ inputChannelData array.
+ @param outputChannelData a set of arrays which need to be filled with the data
+ that should be sent to each outgoing channel of the device.
+ There will be one channel of data for each output channel
+ that was enabled when the audio device was opened (see
+ AudioIODevice::open())
+ The initial contents of the array is undefined, so the
+ callback function must fill all the channels with zeros if
+ its output is silence. Failing to do this could cause quite
+ an unpleasant noise!
+ @param numOutputChannels the number of pointers to channel data in the
+ outputChannelData array.
+ @param numSamples the number of samples in each channel of the input and
+ output arrays. The number of samples will depend on the
+ audio device's buffer size and will usually remain constant,
+ although this isn't guaranteed, so make sure your code can
+ cope with reasonable changes in the buffer size from one
+ callback to the next.
+ */
+ virtual void audioDeviceIOCallback (const float** inputChannelData,
+ int numInputChannels,
+ float** outputChannelData,
+ int numOutputChannels,
+ int numSamples) = 0;
+
+ /** Called to indicate that the device is about to start calling back.
+
+ This will be called just before the audio callbacks begin, either when this
+ callback has just been added to an audio device, or after the device has been
+ restarted because of a sample-rate or block-size change.
+
+ You can use this opportunity to find out the sample rate and block size
+ that the device is going to use by calling the AudioIODevice::getCurrentSampleRate()
+ and AudioIODevice::getCurrentBufferSizeSamples() on the supplied pointer.
+
+ @param device the audio IO device that will be used to drive the callback.
+ Note that if you're going to store this this pointer, it is
+ only valid until the next time that audioDeviceStopped is called.
+ */
+ virtual void audioDeviceAboutToStart (AudioIODevice* device) = 0;
+
+ /** Called to indicate that the device has stopped. */
+ virtual void audioDeviceStopped() = 0;
+
+ /** This can be overridden to be told if the device generates an error while operating.
+ Be aware that this could be called by any thread! And not all devices perform
+ this callback.
+ */
+ virtual void audioDeviceError (const String& errorMessage);
+};
+
+
+//==============================================================================
+/**
+ Base class for an audio device with synchronised input and output channels.
+
+ Subclasses of this are used to implement different protocols such as DirectSound,
+ ASIO, CoreAudio, etc.
+
+ To create one of these, you'll need to use the AudioIODeviceType class - see the
+ documentation for that class for more info.
+
+ For an easier way of managing audio devices and their settings, have a look at the
+ AudioDeviceManager class.
+
+ @see AudioIODeviceType, AudioDeviceManager
+*/
+class JUCE_API AudioIODevice
+{
+public:
+ /** Destructor. */
+ virtual ~AudioIODevice();
+
+ //==============================================================================
+ /** Returns the device's name, (as set in the constructor). */
+ const String& getName() const noexcept { return name; }
+
+ /** Returns the type of the device.
+
+ E.g. "CoreAudio", "ASIO", etc. - this comes from the AudioIODeviceType that created it.
+ */
+ const String& getTypeName() const noexcept { return typeName; }
+
+ //==============================================================================
+ /** Returns the names of all the available output channels on this device.
+ To find out which of these are currently in use, call getActiveOutputChannels().
+ */
+ virtual StringArray getOutputChannelNames() = 0;
+
+ /** Returns the names of all the available input channels on this device.
+ To find out which of these are currently in use, call getActiveInputChannels().
+ */
+ virtual StringArray getInputChannelNames() = 0;
+
+ //==============================================================================
+ /** Returns the set of sample-rates this device supports.
+ @see getCurrentSampleRate
+ */
+ virtual Array getAvailableSampleRates() = 0;
+
+ /** Returns the set of buffer sizes that are available.
+ @see getCurrentBufferSizeSamples, getDefaultBufferSize
+ */
+ virtual Array getAvailableBufferSizes() = 0;
+
+ /** Returns the default buffer-size to use.
+ @returns a number of samples
+ @see getAvailableBufferSizes
+ */
+ virtual int getDefaultBufferSize() = 0;
+
+ //==============================================================================
+ /** Tries to open the device ready to play.
+
+ @param inputChannels a BigInteger in which a set bit indicates that the corresponding
+ input channel should be enabled
+ @param outputChannels a BigInteger in which a set bit indicates that the corresponding
+ output channel should be enabled
+ @param sampleRate the sample rate to try to use - to find out which rates are
+ available, see getAvailableSampleRates()
+ @param bufferSizeSamples the size of i/o buffer to use - to find out the available buffer
+ sizes, see getAvailableBufferSizes()
+ @returns an error description if there's a problem, or an empty string if it succeeds in
+ opening the device
+ @see close
+ */
+ virtual String open (const BigInteger& inputChannels,
+ const BigInteger& outputChannels,
+ double sampleRate,
+ int bufferSizeSamples) = 0;
+
+ /** Closes and releases the device if it's open. */
+ virtual void close() = 0;
+
+ /** Returns true if the device is still open.
+
+ A device might spontaneously close itself if something goes wrong, so this checks if
+ it's still open.
+ */
+ virtual bool isOpen() = 0;
+
+ /** Starts the device actually playing.
+
+ This must be called after the device has been opened.
+
+ @param callback the callback to use for streaming the data.
+ @see AudioIODeviceCallback, open
+ */
+ virtual void start (AudioIODeviceCallback* callback) = 0;
+
+ /** Stops the device playing.
+
+ Once a device has been started, this will stop it. Any pending calls to the
+ callback class will be flushed before this method returns.
+ */
+ virtual void stop() = 0;
+
+ /** Returns true if the device is still calling back.
+
+ The device might mysteriously stop, so this checks whether it's
+ still playing.
+ */
+ virtual bool isPlaying() = 0;
+
+ /** Returns the last error that happened if anything went wrong. */
+ virtual String getLastError() = 0;
+
+ //==============================================================================
+ /** Returns the buffer size that the device is currently using.
+
+ If the device isn't actually open, this value doesn't really mean much.
+ */
+ virtual int getCurrentBufferSizeSamples() = 0;
+
+ /** Returns the sample rate that the device is currently using.
+
+ If the device isn't actually open, this value doesn't really mean much.
+ */
+ virtual double getCurrentSampleRate() = 0;
+
+ /** Returns the device's current physical bit-depth.
+
+ If the device isn't actually open, this value doesn't really mean much.
+ */
+ virtual int getCurrentBitDepth() = 0;
+
+ /** Returns a mask showing which of the available output channels are currently
+ enabled.
+ @see getOutputChannelNames
+ */
+ virtual BigInteger getActiveOutputChannels() const = 0;
+
+ /** Returns a mask showing which of the available input channels are currently
+ enabled.
+ @see getInputChannelNames
+ */
+ virtual BigInteger getActiveInputChannels() const = 0;
+
+ /** Returns the device's output latency.
+
+ This is the delay in samples between a callback getting a block of data, and
+ that data actually getting played.
+ */
+ virtual int getOutputLatencyInSamples() = 0;
+
+ /** Returns the device's input latency.
+
+ This is the delay in samples between some audio actually arriving at the soundcard,
+ and the callback getting passed this block of data.
+ */
+ virtual int getInputLatencyInSamples() = 0;
+
+
+ //==============================================================================
+ /** True if this device can show a pop-up control panel for editing its settings.
+
+ This is generally just true of ASIO devices. If true, you can call showControlPanel()
+ to display it.
+ */
+ virtual bool hasControlPanel() const;
+
+ /** Shows a device-specific control panel if there is one.
+
+ This should only be called for devices which return true from hasControlPanel().
+ */
+ 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:
+ /** Creates a device, setting its name and type member variables. */
+ AudioIODevice (const String& deviceName,
+ const String& typeName);
+
+ /** @internal */
+ String name, typeName;
+};
+
+
+#endif // JUCE_AUDIOIODEVICE_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp
new file mode 100644
index 0000000..80026e7
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp
@@ -0,0 +1,78 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+AudioIODeviceType::AudioIODeviceType (const String& name)
+ : typeName (name)
+{
+}
+
+AudioIODeviceType::~AudioIODeviceType()
+{
+}
+
+//==============================================================================
+void AudioIODeviceType::addListener (Listener* l) { listeners.add (l); }
+void AudioIODeviceType::removeListener (Listener* l) { listeners.remove (l); }
+
+void AudioIODeviceType::callDeviceChangeListeners()
+{
+ listeners.call (&AudioIODeviceType::Listener::audioDeviceListChanged);
+}
+
+//==============================================================================
+#if ! JUCE_MAC
+AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return nullptr; }
+#endif
+
+#if ! JUCE_IOS
+AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { return nullptr; }
+#endif
+
+#if ! (JUCE_WINDOWS && JUCE_WASAPI)
+AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI() { return nullptr; }
+#endif
+
+#if ! (JUCE_WINDOWS && JUCE_DIRECTSOUND)
+AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return nullptr; }
+#endif
+
+#if ! (JUCE_WINDOWS && JUCE_ASIO)
+AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return nullptr; }
+#endif
+
+#if ! (JUCE_LINUX && JUCE_ALSA)
+AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { return nullptr; }
+#endif
+
+#if ! (JUCE_LINUX && JUCE_JACK)
+AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return nullptr; }
+#endif
+
+#if ! JUCE_ANDROID
+AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() { return nullptr; }
+#endif
+
+#if ! (JUCE_ANDROID && JUCE_USE_ANDROID_OPENSLES)
+AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES() { return nullptr; }
+#endif
diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h
new file mode 100644
index 0000000..d6cd99a
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h
@@ -0,0 +1,182 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_AUDIOIODEVICETYPE_H_INCLUDED
+#define JUCE_AUDIOIODEVICETYPE_H_INCLUDED
+
+
+//==============================================================================
+/**
+ Represents a type of audio driver, such as DirectSound, ASIO, CoreAudio, etc.
+
+ To get a list of available audio driver types, use the AudioDeviceManager::createAudioDeviceTypes()
+ method. Each of the objects returned can then be used to list the available
+ devices of that type. E.g.
+ @code
+ OwnedArray types;
+ myAudioDeviceManager.createAudioDeviceTypes (types);
+
+ for (int i = 0; i < types.size(); ++i)
+ {
+ String typeName (types[i]->getTypeName()); // This will be things like "DirectSound", "CoreAudio", etc.
+
+ types[i]->scanForDevices(); // This must be called before getting the list of devices
+
+ StringArray deviceNames (types[i]->getDeviceNames()); // This will now return a list of available devices of this type
+
+ for (int j = 0; j < deviceNames.size(); ++j)
+ {
+ AudioIODevice* device = types[i]->createDevice (deviceNames [j]);
+
+ ...
+ }
+ }
+ @endcode
+
+ For an easier way of managing audio devices and their settings, have a look at the
+ AudioDeviceManager class.
+
+ @see AudioIODevice, AudioDeviceManager
+*/
+class JUCE_API AudioIODeviceType
+{
+public:
+ //==============================================================================
+ /** Returns the name of this type of driver that this object manages.
+
+ This will be something like "DirectSound", "ASIO", "CoreAudio", "ALSA", etc.
+ */
+ const String& getTypeName() const noexcept { return typeName; }
+
+ //==============================================================================
+ /** Refreshes the object's cached list of known devices.
+
+ This must be called at least once before calling getDeviceNames() or any of
+ the other device creation methods.
+ */
+ virtual void scanForDevices() = 0;
+
+ /** Returns the list of available devices of this type.
+
+ The scanForDevices() method must have been called to create this list.
+
+ @param wantInputNames only really used by DirectSound where devices are split up
+ into inputs and outputs, this indicates whether to use
+ the input or output name to refer to a pair of devices.
+ */
+ virtual StringArray getDeviceNames (bool wantInputNames = false) const = 0;
+
+ /** Returns the name of the default device.
+
+ This will be one of the names from the getDeviceNames() list.
+
+ @param forInput if true, this means that a default input device should be
+ returned; if false, it should return the default output
+ */
+ virtual int getDefaultDeviceIndex (bool forInput) const = 0;
+
+ /** Returns the index of a given device in the list of device names.
+ If asInput is true, it shows the index in the inputs list, otherwise it
+ looks for it in the outputs list.
+ */
+ virtual int getIndexOfDevice (AudioIODevice* device, bool asInput) const = 0;
+
+ /** Returns true if two different devices can be used for the input and output.
+ */
+ virtual bool hasSeparateInputsAndOutputs() const = 0;
+
+ /** Creates one of the devices of this type.
+
+ The deviceName must be one of the strings returned by getDeviceNames(), and
+ scanForDevices() must have been called before this method is used.
+ */
+ virtual AudioIODevice* createDevice (const String& outputDeviceName,
+ const String& inputDeviceName) = 0;
+
+ //==============================================================================
+ /**
+ A class for receiving events when audio devices are inserted or removed.
+
+ You can register an AudioIODeviceType::Listener with an~AudioIODeviceType object
+ using the AudioIODeviceType::addListener() method, and it will be called when
+ devices of that type are added or removed.
+
+ @see AudioIODeviceType::addListener, AudioIODeviceType::removeListener
+ */
+ class Listener
+ {
+ public:
+ virtual ~Listener() {}
+
+ /** Called when the list of available audio devices changes. */
+ virtual void audioDeviceListChanged() = 0;
+ };
+
+ /** Adds a listener that will be called when this type of device is added or
+ removed from the system.
+ */
+ void addListener (Listener* listener);
+
+ /** Removes a listener that was previously added with addListener(). */
+ void removeListener (Listener* listener);
+
+ //==============================================================================
+ /** Destructor. */
+ virtual ~AudioIODeviceType();
+
+ //==============================================================================
+ /** Creates a CoreAudio device type if it's available on this platform, or returns null. */
+ static AudioIODeviceType* createAudioIODeviceType_CoreAudio();
+ /** Creates an iOS device type if it's available on this platform, or returns null. */
+ static AudioIODeviceType* createAudioIODeviceType_iOSAudio();
+ /** Creates a WASAPI device type if it's available on this platform, or returns null. */
+ static AudioIODeviceType* createAudioIODeviceType_WASAPI();
+ /** Creates a DirectSound device type if it's available on this platform, or returns null. */
+ static AudioIODeviceType* createAudioIODeviceType_DirectSound();
+ /** Creates an ASIO device type if it's available on this platform, or returns null. */
+ static AudioIODeviceType* createAudioIODeviceType_ASIO();
+ /** Creates an ALSA device type if it's available on this platform, or returns null. */
+ static AudioIODeviceType* createAudioIODeviceType_ALSA();
+ /** Creates a JACK device type if it's available on this platform, or returns null. */
+ static AudioIODeviceType* createAudioIODeviceType_JACK();
+ /** Creates an Android device type if it's available on this platform, or returns null. */
+ static AudioIODeviceType* createAudioIODeviceType_Android();
+ /** Creates an Android OpenSLES device type if it's available on this platform, or returns null. */
+ static AudioIODeviceType* createAudioIODeviceType_OpenSLES();
+
+protected:
+ explicit AudioIODeviceType (const String& typeName);
+
+ /** Synchronously calls all the registered device list change listeners. */
+ void callDeviceChangeListeners();
+
+private:
+ String typeName;
+ ListenerList listeners;
+
+ JUCE_DECLARE_NON_COPYABLE (AudioIODeviceType)
+};
+
+
+#endif // JUCE_AUDIOIODEVICETYPE_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_SystemAudioVolume.h b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_SystemAudioVolume.h
new file mode 100644
index 0000000..689e1f2
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_SystemAudioVolume.h
@@ -0,0 +1,61 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_SYSTEMAUDIOVOLUME_H_INCLUDED
+#define JUCE_SYSTEMAUDIOVOLUME_H_INCLUDED
+
+
+//==============================================================================
+/**
+ Contains functions to control the system's master volume.
+*/
+class JUCE_API SystemAudioVolume
+{
+public:
+ //==============================================================================
+ /** Returns the operating system's current volume level in the range 0 to 1.0 */
+ static float JUCE_CALLTYPE getGain();
+
+ /** Attempts to set the operating system's current volume level.
+ @param newGain the level, between 0 and 1.0
+ @returns true if the operation succeeds
+ */
+ static bool JUCE_CALLTYPE setGain (float newGain);
+
+ /** Returns true if the system's audio output is currently muted. */
+ static bool JUCE_CALLTYPE isMuted();
+
+ /** Attempts to mute the operating system's audio output.
+ @param shouldBeMuted true if you want it to be muted
+ @returns true if the operation succeeds
+ */
+ static bool JUCE_CALLTYPE setMuted (bool shouldBeMuted);
+
+private:
+ SystemAudioVolume(); // Don't instantiate this class, just call its static fns.
+ JUCE_DECLARE_NON_COPYABLE (SystemAudioVolume)
+};
+
+
+#endif // JUCE_SYSTEMAUDIOVOLUME_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.cpp b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.cpp
new file mode 100644
index 0000000..a9733db
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.cpp
@@ -0,0 +1,227 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#if defined (JUCE_AUDIO_DEVICES_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE
+ /* When you add this cpp file to your project, you mustn't include it in a file where you've
+ already included any other headers - just put it inside a file on its own, possibly with your config
+ flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
+ header files that the compiler may be using.
+ */
+ #error "Incorrect use of JUCE cpp file"
+#endif
+
+// Your project must contain an AppConfig.h file with your project-specific settings in it,
+// and your header search path must make it accessible to the module's files.
+#include "AppConfig.h"
+
+#include "../juce_core/native/juce_BasicNativeHeaders.h"
+#include "juce_audio_devices.h"
+
+//==============================================================================
+#if JUCE_MAC
+ #define Point CarbonDummyPointName
+ #define Component CarbonDummyCompName
+ #import
+ #import
+ #import
+ #import
+ #undef Point
+ #undef Component
+
+#elif JUCE_IOS
+ #import
+ #import
+ #import
+
+//==============================================================================
+#elif JUCE_WINDOWS
+ #if JUCE_WASAPI
+ #include
+ #endif
+
+ #if JUCE_ASIO
+ /* This is very frustrating - we only need to use a handful of definitions from
+ a couple of the header files in Steinberg's ASIO SDK, and it'd be easy to copy
+ about 30 lines of code into this cpp file to create a fully stand-alone ASIO
+ implementation...
+
+ ..unfortunately that would break Steinberg's license agreement for use of
+ their SDK, so I'm not allowed to do this.
+
+ This means that anyone who wants to use JUCE's ASIO abilities will have to:
+
+ 1) Agree to Steinberg's licensing terms and download the ASIO SDK
+ (see www.steinberg.net/Steinberg/Developers.asp).
+
+ 2) Enable this code with a global definition #define JUCE_ASIO 1.
+
+ 3) Make sure that your header search path contains the iasiodrv.h file that
+ comes with the SDK. (Only about a handful of the SDK header files are actually
+ needed - so to simplify things, you could just copy these into your JUCE directory).
+ */
+ #include
+ #endif
+
+ #if JUCE_USE_CDBURNER
+ /* You'll need the Platform SDK for these headers - if you don't have it and don't
+ need to use CD-burning, then you might just want to set the JUCE_USE_CDBURNER flag
+ to 0, to avoid these includes.
+ */
+ #include
+ #include
+ #endif
+
+//==============================================================================
+#elif JUCE_LINUX
+ #if JUCE_ALSA
+ /* Got an include error here? If so, you've either not got ALSA installed, or you've
+ not got your paths set up correctly to find its header files.
+
+ The package you need to install to get ASLA support is "libasound2-dev".
+
+ If you don't have the ALSA library and don't want to build Juce with audio support,
+ just set the JUCE_ALSA flag to 0.
+ */
+ #include
+ #endif
+
+ #if JUCE_JACK
+ /* Got an include error here? If so, you've either not got jack-audio-connection-kit
+ installed, or you've not got your paths set up correctly to find its header files.
+
+ The package you need to install to get JACK support is "libjack-dev".
+
+ If you don't have the jack-audio-connection-kit library and don't want to build
+ Juce with low latency audio support, just set the JUCE_JACK flag to 0.
+ */
+ #include
+ #endif
+ #undef SIZEOF
+
+//==============================================================================
+#elif JUCE_ANDROID
+
+ #if JUCE_USE_ANDROID_OPENSLES
+ #include
+ #include
+ #include
+ #endif
+
+#endif
+
+namespace juce
+{
+
+#include "audio_io/juce_AudioDeviceManager.cpp"
+#include "audio_io/juce_AudioIODevice.cpp"
+#include "audio_io/juce_AudioIODeviceType.cpp"
+#include "midi_io/juce_MidiMessageCollector.cpp"
+#include "midi_io/juce_MidiOutput.cpp"
+#include "audio_cd/juce_AudioCDReader.cpp"
+#include "sources/juce_AudioSourcePlayer.cpp"
+#include "sources/juce_AudioTransportSource.cpp"
+#include "native/juce_MidiDataConcatenator.h"
+
+//==============================================================================
+#if JUCE_MAC
+ #include "../juce_core/native/juce_osx_ObjCHelpers.h"
+ #include "native/juce_mac_CoreAudio.cpp"
+ #include "native/juce_mac_CoreMidi.cpp"
+
+ #if JUCE_USE_CDREADER
+ #include "native/juce_mac_AudioCDReader.mm"
+ #endif
+
+ #if JUCE_USE_CDBURNER
+ #include "native/juce_mac_AudioCDBurner.mm"
+ #endif
+
+//==============================================================================
+#elif JUCE_IOS
+ #include "native/juce_ios_Audio.cpp"
+ #include "native/juce_mac_CoreMidi.cpp"
+
+//==============================================================================
+#elif JUCE_WINDOWS
+ #include "../juce_core/native/juce_win32_ComSmartPtr.h"
+ #include "../juce_events/native/juce_win32_HiddenMessageWindow.h"
+
+ #if JUCE_WASAPI
+ #include "native/juce_win32_WASAPI.cpp"
+ #endif
+
+ #if JUCE_DIRECTSOUND
+ #include "native/juce_win32_DirectSound.cpp"
+ #endif
+
+ #include "native/juce_win32_Midi.cpp"
+
+ #if JUCE_ASIO
+ #include "native/juce_win32_ASIO.cpp"
+ #endif
+
+ #if JUCE_USE_CDREADER
+ #include "native/juce_win32_AudioCDReader.cpp"
+ #endif
+
+ #if JUCE_USE_CDBURNER
+ #include "native/juce_win32_AudioCDBurner.cpp"
+ #endif
+
+//==============================================================================
+#elif JUCE_LINUX
+ #if JUCE_ALSA
+ #include "native/juce_linux_ALSA.cpp"
+ #endif
+
+ #include "native/juce_linux_Midi.cpp"
+
+ #if JUCE_JACK
+ #include "native/juce_linux_JackAudio.cpp"
+ #endif
+
+ #if JUCE_USE_CDREADER
+ #include "native/juce_linux_AudioCDReader.cpp"
+ #endif
+
+//==============================================================================
+#elif JUCE_ANDROID
+ #include "../juce_core/native/juce_android_JNIHelpers.h"
+ #include "native/juce_android_Audio.cpp"
+ #include "native/juce_android_Midi.cpp"
+
+ #if JUCE_USE_ANDROID_OPENSLES
+ #include "native/juce_android_OpenSL.cpp"
+ #endif
+
+#endif
+
+#if ! JUCE_SYSTEMAUDIOVOL_IMPLEMENTED
+ // None of these methods are available. (On Windows you might need to enable WASAPI for this)
+ float JUCE_CALLTYPE SystemAudioVolume::getGain() { jassertfalse; return 0.0f; }
+ bool JUCE_CALLTYPE SystemAudioVolume::setGain (float) { jassertfalse; return false; }
+ bool JUCE_CALLTYPE SystemAudioVolume::isMuted() { jassertfalse; return false; }
+ bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool) { jassertfalse; return false; }
+#endif
+}
diff --git a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h
new file mode 100644
index 0000000..b8528d5
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h
@@ -0,0 +1,117 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_AUDIO_DEVICES_H_INCLUDED
+#define JUCE_AUDIO_DEVICES_H_INCLUDED
+
+#include "../juce_events/juce_events.h"
+#include "../juce_audio_basics/juce_audio_basics.h"
+#include "../juce_audio_formats/juce_audio_formats.h"
+
+//=============================================================================
+/** Config: JUCE_ASIO
+ Enables ASIO audio devices (MS Windows only).
+ Turning this on means that you'll need to have the Steinberg ASIO SDK installed
+ on your Windows build machine.
+
+ See the comments in the ASIOAudioIODevice class's header file for more
+ info about this.
+*/
+#ifndef JUCE_ASIO
+ #define JUCE_ASIO 0
+#endif
+
+/** Config: JUCE_WASAPI
+ Enables WASAPI audio devices (Windows Vista and above).
+*/
+#ifndef JUCE_WASAPI
+ #define JUCE_WASAPI 1
+#endif
+
+/** Config: JUCE_DIRECTSOUND
+ Enables DirectSound audio (MS Windows only).
+*/
+#ifndef JUCE_DIRECTSOUND
+ #define JUCE_DIRECTSOUND 1
+#endif
+
+/** Config: JUCE_ALSA
+ Enables ALSA audio devices (Linux only).
+*/
+#ifndef JUCE_ALSA
+ #define JUCE_ALSA 1
+#endif
+
+/** Config: JUCE_JACK
+ Enables JACK audio devices (Linux only).
+*/
+#ifndef JUCE_JACK
+ #define JUCE_JACK 0
+#endif
+
+/** Config: JUCE_USE_ANDROID_OPENSLES
+ Enables OpenSLES devices (Android only).
+*/
+#ifndef JUCE_USE_ANDROID_OPENSLES
+ #if JUCE_ANDROID_API_VERSION > 8
+ #define JUCE_USE_ANDROID_OPENSLES 1
+ #else
+ #define JUCE_USE_ANDROID_OPENSLES 0
+ #endif
+#endif
+
+//=============================================================================
+/** Config: JUCE_USE_CDREADER
+ Enables the AudioCDReader class (on supported platforms).
+*/
+#ifndef JUCE_USE_CDREADER
+ #define JUCE_USE_CDREADER 0
+#endif
+
+/** Config: JUCE_USE_CDBURNER
+ Enables the AudioCDBurner class (on supported platforms).
+*/
+#ifndef JUCE_USE_CDBURNER
+ #define JUCE_USE_CDBURNER 0
+#endif
+
+//=============================================================================
+namespace juce
+{
+
+#include "audio_io/juce_AudioIODevice.h"
+#include "audio_io/juce_AudioIODeviceType.h"
+#include "audio_io/juce_SystemAudioVolume.h"
+#include "midi_io/juce_MidiInput.h"
+#include "midi_io/juce_MidiMessageCollector.h"
+#include "midi_io/juce_MidiOutput.h"
+#include "sources/juce_AudioSourcePlayer.h"
+#include "sources/juce_AudioTransportSource.h"
+#include "audio_cd/juce_AudioCDBurner.h"
+#include "audio_cd/juce_AudioCDReader.h"
+#include "audio_io/juce_AudioDeviceManager.h"
+
+}
+
+#endif // JUCE_AUDIO_DEVICES_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.mm b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.mm
new file mode 100644
index 0000000..a135119
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.mm
@@ -0,0 +1,25 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#include "juce_audio_devices.cpp"
diff --git a/JuceLibraryCode/modules/juce_audio_devices/juce_module_info b/JuceLibraryCode/modules/juce_audio_devices/juce_module_info
new file mode 100644
index 0000000..6712c58
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/juce_module_info
@@ -0,0 +1,28 @@
+{
+ "id": "juce_audio_devices",
+ "name": "JUCE audio and midi I/O device classes",
+ "version": "3.1.1",
+ "description": "Classes to play and record from audio and midi i/o devices.",
+ "website": "http://www.juce.com/juce",
+ "license": "GPL/Commercial",
+
+ "dependencies": [ { "id": "juce_audio_basics", "version": "matching" },
+ { "id": "juce_audio_formats", "version": "matching" },
+ { "id": "juce_events", "version": "matching" } ],
+
+ "include": "juce_audio_devices.h",
+
+ "compile": [ { "file": "juce_audio_devices.cpp", "target": "! xcode" },
+ { "file": "juce_audio_devices.mm", "target": "xcode" } ],
+
+ "browse": [ "audio_io/*",
+ "midi_io/*",
+ "sources/*",
+ "audio_cd/*",
+ "native/*" ],
+
+ "OSXFrameworks": "CoreAudio CoreMIDI DiscRecording",
+ "iOSFrameworks": "AudioToolbox CoreMIDI",
+ "LinuxLibs": "asound",
+ "mingwLibs": "winmm"
+}
diff --git a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiInput.h b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiInput.h
new file mode 100644
index 0000000..6308020
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiInput.h
@@ -0,0 +1,182 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_MIDIINPUT_H_INCLUDED
+#define JUCE_MIDIINPUT_H_INCLUDED
+
+class MidiInput;
+
+
+//==============================================================================
+/**
+ Receives incoming messages from a physical MIDI input device.
+
+ This class is overridden to handle incoming midi messages. See the MidiInput
+ class for more details.
+
+ @see MidiInput
+*/
+class JUCE_API MidiInputCallback
+{
+public:
+ /** Destructor. */
+ virtual ~MidiInputCallback() {}
+
+
+ /** Receives an incoming message.
+
+ A MidiInput object will call this method when a midi event arrives. It'll be
+ called on a high-priority system thread, so avoid doing anything time-consuming
+ in here, and avoid making any UI calls. You might find the MidiBuffer class helpful
+ for queueing incoming messages for use later.
+
+ @param source the MidiInput object that generated the message
+ @param message the incoming message. The message's timestamp is set to a value
+ equivalent to (Time::getMillisecondCounter() / 1000.0) to specify the
+ time when the message arrived.
+ */
+ virtual void handleIncomingMidiMessage (MidiInput* source,
+ const MidiMessage& message) = 0;
+
+ /** Notification sent each time a packet of a multi-packet sysex message arrives.
+
+ If a long sysex message is broken up into multiple packets, this callback is made
+ for each packet that arrives until the message is finished, at which point
+ the normal handleIncomingMidiMessage() callback will be made with the entire
+ message.
+
+ The message passed in will contain the start of a sysex, but won't be finished
+ with the terminating 0xf7 byte.
+ */
+ virtual void handlePartialSysexMessage (MidiInput* source,
+ const uint8* messageData,
+ int numBytesSoFar,
+ double timestamp)
+ {
+ // (this bit is just to avoid compiler warnings about unused variables)
+ (void) source; (void) messageData; (void) numBytesSoFar; (void) timestamp;
+ }
+};
+
+//==============================================================================
+/**
+ Represents a midi input device.
+
+ To create one of these, use the static getDevices() method to find out what inputs are
+ available, and then use the openDevice() method to try to open one.
+
+ @see MidiOutput
+*/
+class JUCE_API MidiInput
+{
+public:
+ //==============================================================================
+ /** Returns a list of the available midi input devices.
+
+ You can open one of the devices by passing its index into the
+ openDevice() method.
+
+ @see getDefaultDeviceIndex, openDevice
+ */
+ static StringArray getDevices();
+
+ /** Returns the index of the default midi input device to use.
+
+ This refers to the index in the list returned by getDevices().
+ */
+ static int getDefaultDeviceIndex();
+
+ /** Tries to open one of the midi input devices.
+
+ This will return a MidiInput object if it manages to open it. You can then
+ call start() and stop() on this device, and delete it when no longer needed.
+
+ If the device can't be opened, this will return a null pointer.
+
+ @param deviceIndex the index of a device from the list returned by getDevices()
+ @param callback the object that will receive the midi messages from this device.
+
+ @see MidiInputCallback, getDevices
+ */
+ static MidiInput* openDevice (int deviceIndex,
+ MidiInputCallback* callback);
+
+ #if JUCE_LINUX || JUCE_MAC || JUCE_IOS || DOXYGEN
+ /** This will try to create a new midi input device (Not available on Windows).
+
+ This will attempt to create a new midi input device with the specified name,
+ for other apps to connect to.
+
+ Returns nullptr if a device can't be created.
+
+ @param deviceName the name to use for the new device
+ @param callback the object that will receive the midi messages from this device.
+ */
+ static MidiInput* createNewDevice (const String& deviceName,
+ MidiInputCallback* callback);
+ #endif
+
+ //==============================================================================
+ /** Destructor. */
+ virtual ~MidiInput();
+
+ /** Returns the name of this device. */
+ const String& getName() const noexcept { return name; }
+
+ /** Allows you to set a custom name for the device, in case you don't like the name
+ it was given when created.
+ */
+ void setName (const String& newName) noexcept { name = newName; }
+
+ //==============================================================================
+ /** Starts the device running.
+
+ After calling this, the device will start sending midi messages to the
+ MidiInputCallback object that was specified when the openDevice() method
+ was called.
+
+ @see stop
+ */
+ virtual void start();
+
+ /** Stops the device running.
+
+ @see start
+ */
+ virtual void stop();
+
+protected:
+ //==============================================================================
+ String name;
+ void* internal;
+
+ explicit MidiInput (const String& name);
+
+private:
+ //==============================================================================
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInput)
+};
+
+
+#endif // JUCE_MIDIINPUT_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.cpp b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.cpp
new file mode 100644
index 0000000..28e81c0
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.cpp
@@ -0,0 +1,153 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+MidiMessageCollector::MidiMessageCollector()
+ : lastCallbackTime (0),
+ sampleRate (44100.0001)
+{
+}
+
+MidiMessageCollector::~MidiMessageCollector()
+{
+}
+
+//==============================================================================
+void MidiMessageCollector::reset (const double sampleRate_)
+{
+ jassert (sampleRate_ > 0);
+
+ const ScopedLock sl (midiCallbackLock);
+ sampleRate = sampleRate_;
+ incomingMessages.clear();
+ lastCallbackTime = Time::getMillisecondCounterHiRes();
+}
+
+void MidiMessageCollector::addMessageToQueue (const MidiMessage& message)
+{
+ // you need to call reset() to set the correct sample rate before using this object
+ jassert (sampleRate != 44100.0001);
+
+ // the messages that come in here need to be time-stamped correctly - see MidiInput
+ // for details of what the number should be.
+ jassert (message.getTimeStamp() != 0);
+
+ const ScopedLock sl (midiCallbackLock);
+
+ const int sampleNumber
+ = (int) ((message.getTimeStamp() - 0.001 * lastCallbackTime) * sampleRate);
+
+ incomingMessages.addEvent (message, sampleNumber);
+
+ // if the messages don't get used for over a second, we'd better
+ // get rid of any old ones to avoid the queue getting too big
+ if (sampleNumber > sampleRate)
+ incomingMessages.clear (0, sampleNumber - (int) sampleRate);
+}
+
+void MidiMessageCollector::removeNextBlockOfMessages (MidiBuffer& destBuffer,
+ const int numSamples)
+{
+ // you need to call reset() to set the correct sample rate before using this object
+ jassert (sampleRate != 44100.0001);
+ jassert (numSamples > 0);
+
+ const double timeNow = Time::getMillisecondCounterHiRes();
+ const double msElapsed = timeNow - lastCallbackTime;
+
+ const ScopedLock sl (midiCallbackLock);
+ lastCallbackTime = timeNow;
+
+ if (! incomingMessages.isEmpty())
+ {
+ int numSourceSamples = jmax (1, roundToInt (msElapsed * 0.001 * sampleRate));
+
+ int startSample = 0;
+ int scale = 1 << 16;
+
+ const uint8* midiData;
+ int numBytes, samplePosition;
+
+ MidiBuffer::Iterator iter (incomingMessages);
+
+ if (numSourceSamples > numSamples)
+ {
+ // if our list of events is longer than the buffer we're being
+ // asked for, scale them down to squeeze them all in..
+ const int maxBlockLengthToUse = numSamples << 5;
+
+ if (numSourceSamples > maxBlockLengthToUse)
+ {
+ startSample = numSourceSamples - maxBlockLengthToUse;
+ numSourceSamples = maxBlockLengthToUse;
+ iter.setNextSamplePosition (startSample);
+ }
+
+ scale = (numSamples << 10) / numSourceSamples;
+
+ while (iter.getNextEvent (midiData, numBytes, samplePosition))
+ {
+ samplePosition = ((samplePosition - startSample) * scale) >> 10;
+
+ destBuffer.addEvent (midiData, numBytes,
+ jlimit (0, numSamples - 1, samplePosition));
+ }
+ }
+ else
+ {
+ // if our event list is shorter than the number we need, put them
+ // towards the end of the buffer
+ startSample = numSamples - numSourceSamples;
+
+ while (iter.getNextEvent (midiData, numBytes, samplePosition))
+ {
+ destBuffer.addEvent (midiData, numBytes,
+ jlimit (0, numSamples - 1, samplePosition + startSample));
+ }
+ }
+
+ incomingMessages.clear();
+ }
+}
+
+//==============================================================================
+void MidiMessageCollector::handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity)
+{
+ MidiMessage m (MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity));
+ m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
+
+ addMessageToQueue (m);
+}
+
+void MidiMessageCollector::handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber)
+{
+ MidiMessage m (MidiMessage::noteOff (midiChannel, midiNoteNumber));
+ m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
+
+ addMessageToQueue (m);
+}
+
+void MidiMessageCollector::handleIncomingMidiMessage (MidiInput*, const MidiMessage& message)
+{
+ addMessageToQueue (message);
+}
diff --git a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.h b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.h
new file mode 100644
index 0000000..5b16737
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.h
@@ -0,0 +1,104 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_MIDIMESSAGECOLLECTOR_H_INCLUDED
+#define JUCE_MIDIMESSAGECOLLECTOR_H_INCLUDED
+
+
+//==============================================================================
+/**
+ Collects incoming realtime MIDI messages and turns them into blocks suitable for
+ processing by a block-based audio callback.
+
+ The class can also be used as either a MidiKeyboardStateListener or a MidiInputCallback
+ so it can easily use a midi input or keyboard component as its source.
+
+ @see MidiMessage, MidiInput
+*/
+class JUCE_API MidiMessageCollector : public MidiKeyboardStateListener,
+ public MidiInputCallback
+{
+public:
+ //==============================================================================
+ /** Creates a MidiMessageCollector. */
+ MidiMessageCollector();
+
+ /** Destructor. */
+ ~MidiMessageCollector();
+
+ //==============================================================================
+ /** Clears any messages from the queue.
+
+ You need to call this method before starting to use the collector, so that
+ it knows the correct sample rate to use.
+ */
+ void reset (double sampleRate);
+
+ /** Takes an incoming real-time message and adds it to the queue.
+
+ The message's timestamp is taken, and it will be ready for retrieval as part
+ of the block returned by the next call to removeNextBlockOfMessages().
+
+ This method is fully thread-safe when overlapping calls are made with
+ removeNextBlockOfMessages().
+ */
+ void addMessageToQueue (const MidiMessage& message);
+
+ /** Removes all the pending messages from the queue as a buffer.
+
+ This will also correct the messages' timestamps to make sure they're in
+ the range 0 to numSamples - 1.
+
+ This call should be made regularly by something like an audio processing
+ callback, because the time that it happens is used in calculating the
+ midi event positions.
+
+ This method is fully thread-safe when overlapping calls are made with
+ addMessageToQueue().
+
+ Precondition: numSamples must be greater than 0.
+ */
+ void removeNextBlockOfMessages (MidiBuffer& destBuffer, int numSamples);
+
+
+ //==============================================================================
+ /** @internal */
+ void handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override;
+ /** @internal */
+ void handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber) override;
+ /** @internal */
+ void handleIncomingMidiMessage (MidiInput*, const MidiMessage&) override;
+
+private:
+ //==============================================================================
+ double lastCallbackTime;
+ CriticalSection midiCallbackLock;
+ MidiBuffer incomingMessages;
+ double sampleRate;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiMessageCollector)
+};
+
+
+#endif // JUCE_MIDIMESSAGECOLLECTOR_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.cpp b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.cpp
new file mode 100644
index 0000000..0af7d1b
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.cpp
@@ -0,0 +1,162 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+struct MidiOutput::PendingMessage
+{
+ PendingMessage (const void* const data, const int len, const double timeStamp)
+ : message (data, len, timeStamp)
+ {}
+
+ MidiMessage message;
+ PendingMessage* next;
+};
+
+MidiOutput::MidiOutput()
+ : Thread ("midi out"),
+ internal (nullptr),
+ firstMessage (nullptr)
+{
+}
+
+void MidiOutput::sendBlockOfMessages (const MidiBuffer& buffer,
+ const double millisecondCounterToStartAt,
+ double samplesPerSecondForBuffer)
+{
+ // You've got to call startBackgroundThread() for this to actually work..
+ jassert (isThreadRunning());
+
+ // this needs to be a value in the future - RTFM for this method!
+ jassert (millisecondCounterToStartAt > 0);
+
+ const double timeScaleFactor = 1000.0 / samplesPerSecondForBuffer;
+
+ MidiBuffer::Iterator i (buffer);
+
+ const uint8* data;
+ int len, time;
+
+ while (i.getNextEvent (data, len, time))
+ {
+ const double eventTime = millisecondCounterToStartAt + timeScaleFactor * time;
+
+ PendingMessage* const m = new PendingMessage (data, len, eventTime);
+
+ const ScopedLock sl (lock);
+
+ if (firstMessage == nullptr || firstMessage->message.getTimeStamp() > eventTime)
+ {
+ m->next = firstMessage;
+ firstMessage = m;
+ }
+ else
+ {
+ PendingMessage* mm = firstMessage;
+
+ while (mm->next != nullptr && mm->next->message.getTimeStamp() <= eventTime)
+ mm = mm->next;
+
+ m->next = mm->next;
+ mm->next = m;
+ }
+ }
+
+ notify();
+}
+
+void MidiOutput::clearAllPendingMessages()
+{
+ const ScopedLock sl (lock);
+
+ while (firstMessage != nullptr)
+ {
+ PendingMessage* const m = firstMessage;
+ firstMessage = firstMessage->next;
+ delete m;
+ }
+}
+
+void MidiOutput::startBackgroundThread()
+{
+ startThread (9);
+}
+
+void MidiOutput::stopBackgroundThread()
+{
+ stopThread (5000);
+}
+
+void MidiOutput::run()
+{
+ while (! threadShouldExit())
+ {
+ uint32 now = Time::getMillisecondCounter();
+ uint32 eventTime = 0;
+ uint32 timeToWait = 500;
+
+ PendingMessage* message;
+
+ {
+ const ScopedLock sl (lock);
+ message = firstMessage;
+
+ if (message != nullptr)
+ {
+ eventTime = (uint32) roundToInt (message->message.getTimeStamp());
+
+ if (eventTime > now + 20)
+ {
+ timeToWait = eventTime - (now + 20);
+ message = nullptr;
+ }
+ else
+ {
+ firstMessage = message->next;
+ }
+ }
+ }
+
+ if (message != nullptr)
+ {
+ const ScopedPointer messageDeleter (message);
+
+ if (eventTime > now)
+ {
+ Time::waitForMillisecondCounter (eventTime);
+
+ if (threadShouldExit())
+ break;
+ }
+
+ if (eventTime > now - 200)
+ sendMessageNow (message->message);
+ }
+ else
+ {
+ jassert (timeToWait < 1000 * 30);
+ wait ((int) timeToWait);
+ }
+ }
+
+ clearAllPendingMessages();
+}
diff --git a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.h b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.h
new file mode 100644
index 0000000..c668f8a
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.h
@@ -0,0 +1,147 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_MIDIOUTPUT_H_INCLUDED
+#define JUCE_MIDIOUTPUT_H_INCLUDED
+
+
+//==============================================================================
+/**
+ Controls a physical MIDI output device.
+
+ To create one of these, use the static getDevices() method to get a list of the
+ available output devices, then use the openDevice() method to try to open one.
+
+ @see MidiInput
+*/
+class JUCE_API MidiOutput : private Thread
+{
+public:
+ //==============================================================================
+ /** Returns a list of the available midi output devices.
+
+ You can open one of the devices by passing its index into the
+ openDevice() method.
+
+ @see getDefaultDeviceIndex, openDevice
+ */
+ static StringArray getDevices();
+
+ /** Returns the index of the default midi output device to use.
+
+ This refers to the index in the list returned by getDevices().
+ */
+ static int getDefaultDeviceIndex();
+
+ /** Tries to open one of the midi output devices.
+
+ This will return a MidiOutput object if it manages to open it. You can then
+ send messages to this device, and delete it when no longer needed.
+
+ If the device can't be opened, this will return a null pointer.
+
+ @param deviceIndex the index of a device from the list returned by getDevices()
+ @see getDevices
+ */
+ static MidiOutput* openDevice (int deviceIndex);
+
+
+ #if JUCE_LINUX || JUCE_MAC || JUCE_IOS || DOXYGEN
+ /** This will try to create a new midi output device (Not available on Windows).
+
+ This will attempt to create a new midi output device that other apps can connect
+ to and use as their midi input.
+
+ Returns nullptr if a device can't be created.
+
+ @param deviceName the name to use for the new device
+ */
+ static MidiOutput* createNewDevice (const String& deviceName);
+ #endif
+
+ //==============================================================================
+ /** Destructor. */
+ virtual ~MidiOutput();
+
+ /** Makes this device output a midi message.
+
+ @see MidiMessage
+ */
+ virtual void sendMessageNow (const MidiMessage& message);
+
+ //==============================================================================
+ /** This lets you supply a block of messages that will be sent out at some point
+ in the future.
+
+ The MidiOutput class has an internal thread that can send out timestamped
+ messages - this appends a set of messages to its internal buffer, ready for
+ sending.
+
+ This will only work if you've already started the thread with startBackgroundThread().
+
+ A time is supplied, at which the block of messages should be sent. This time uses
+ the same time base as Time::getMillisecondCounter(), and must be in the future.
+
+ The samplesPerSecondForBuffer parameter indicates the number of samples per second
+ used by the MidiBuffer. Each event in a MidiBuffer has a sample position, and the
+ samplesPerSecondForBuffer value is needed to convert this sample position to a
+ real time.
+ */
+ virtual void sendBlockOfMessages (const MidiBuffer& buffer,
+ double millisecondCounterToStartAt,
+ double samplesPerSecondForBuffer);
+
+ /** Gets rid of any midi messages that had been added by sendBlockOfMessages().
+ */
+ virtual void clearAllPendingMessages();
+
+ /** Starts up a background thread so that the device can send blocks of data.
+
+ Call this to get the device ready, before using sendBlockOfMessages().
+ */
+ virtual void startBackgroundThread();
+
+ /** Stops the background thread, and clears any pending midi events.
+
+ @see startBackgroundThread
+ */
+ virtual void stopBackgroundThread();
+
+
+protected:
+ //==============================================================================
+ void* internal;
+ CriticalSection lock;
+ struct PendingMessage;
+ PendingMessage* firstMessage;
+
+ MidiOutput();
+ void run() override;
+
+private:
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutput)
+};
+
+
+#endif // JUCE_MIDIOUTPUT_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h b/JuceLibraryCode/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h
new file mode 100644
index 0000000..4dc70b9
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h
@@ -0,0 +1,187 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_MIDIDATACONCATENATOR_H_INCLUDED
+#define JUCE_MIDIDATACONCATENATOR_H_INCLUDED
+
+//==============================================================================
+/**
+ Helper class that takes chunks of incoming midi bytes, packages them into
+ messages, and dispatches them to a midi callback.
+*/
+class MidiDataConcatenator
+{
+public:
+ //==============================================================================
+ MidiDataConcatenator (const int initialBufferSize)
+ : pendingData ((size_t) initialBufferSize),
+ pendingDataTime (0), pendingBytes (0), runningStatus (0)
+ {
+ }
+
+ void reset()
+ {
+ pendingBytes = 0;
+ runningStatus = 0;
+ pendingDataTime = 0;
+ }
+
+ template
+ void pushMidiData (const void* inputData, int numBytes, double time,
+ UserDataType* input, CallbackType& callback)
+ {
+ const uint8* d = static_cast (inputData);
+
+ while (numBytes > 0)
+ {
+ if (pendingBytes > 0 || d[0] == 0xf0)
+ {
+ processSysex (d, numBytes, time, input, callback);
+ runningStatus = 0;
+ }
+ else
+ {
+ int len = 0;
+ uint8 data[3];
+
+ while (numBytes > 0)
+ {
+ // If there's a realtime message embedded in the middle of
+ // the normal message, handle it now..
+ if (*d >= 0xf8 && *d <= 0xfe)
+ {
+ const MidiMessage m (*d++, time);
+ callback.handleIncomingMidiMessage (input, m);
+ --numBytes;
+ }
+ else
+ {
+ if (len == 0 && *d < 0x80 && runningStatus >= 0x80)
+ data[len++] = runningStatus;
+
+ data[len++] = *d++;
+ --numBytes;
+
+ if (len >= MidiMessage::getMessageLengthFromFirstByte (data[0]))
+ break;
+ }
+ }
+
+ if (len > 0)
+ {
+ int used = 0;
+ const MidiMessage m (data, len, used, 0, time);
+
+ if (used <= 0)
+ break; // malformed message..
+
+ jassert (used == len);
+ callback.handleIncomingMidiMessage (input, m);
+ runningStatus = data[0];
+ }
+ }
+ }
+ }
+
+private:
+ template
+ void processSysex (const uint8*& d, int& numBytes, double time,
+ UserDataType* input, CallbackType& callback)
+ {
+ if (*d == 0xf0)
+ {
+ pendingBytes = 0;
+ pendingDataTime = time;
+ }
+
+ pendingData.ensureSize ((size_t) (pendingBytes + numBytes), false);
+ uint8* totalMessage = static_cast (pendingData.getData());
+ uint8* dest = totalMessage + pendingBytes;
+
+ do
+ {
+ if (pendingBytes > 0 && *d >= 0x80)
+ {
+ if (*d == 0xf7)
+ {
+ *dest++ = *d++;
+ ++pendingBytes;
+ --numBytes;
+ break;
+ }
+
+ if (*d >= 0xfa || *d == 0xf8)
+ {
+ callback.handleIncomingMidiMessage (input, MidiMessage (*d, time));
+ ++d;
+ --numBytes;
+ }
+ else
+ {
+ pendingBytes = 0;
+ int used = 0;
+ const MidiMessage m (d, numBytes, used, 0, time);
+
+ if (used > 0)
+ {
+ callback.handleIncomingMidiMessage (input, m);
+ numBytes -= used;
+ d += used;
+ }
+
+ break;
+ }
+ }
+ else
+ {
+ *dest++ = *d++;
+ ++pendingBytes;
+ --numBytes;
+ }
+ }
+ while (numBytes > 0);
+
+ if (pendingBytes > 0)
+ {
+ if (totalMessage [pendingBytes - 1] == 0xf7)
+ {
+ callback.handleIncomingMidiMessage (input, MidiMessage (totalMessage, pendingBytes, pendingDataTime));
+ pendingBytes = 0;
+ }
+ else
+ {
+ callback.handlePartialSysexMessage (input, totalMessage, pendingBytes, pendingDataTime);
+ }
+ }
+ }
+
+ MemoryBlock pendingData;
+ double pendingDataTime;
+ int pendingBytes;
+ uint8 runningStatus;
+
+ JUCE_DECLARE_NON_COPYABLE (MidiDataConcatenator)
+};
+
+#endif // JUCE_MIDIDATACONCATENATOR_H_INCLUDED
diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp
new file mode 100644
index 0000000..80711da
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp
@@ -0,0 +1,450 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+//==============================================================================
+#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
+ STATICMETHOD (getMinBufferSize, "getMinBufferSize", "(III)I") \
+ STATICMETHOD (getNativeOutputSampleRate, "getNativeOutputSampleRate", "(I)I") \
+ METHOD (constructor, "", "(IIIIII)V") \
+ METHOD (getState, "getState", "()I") \
+ METHOD (play, "play", "()V") \
+ METHOD (stop, "stop", "()V") \
+ METHOD (release, "release", "()V") \
+ METHOD (flush, "flush", "()V") \
+ METHOD (write, "write", "([SII)I") \
+
+DECLARE_JNI_CLASS (AudioTrack, "android/media/AudioTrack");
+#undef JNI_CLASS_MEMBERS
+
+//==============================================================================
+#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
+ STATICMETHOD (getMinBufferSize, "getMinBufferSize", "(III)I") \
+ METHOD (constructor, "", "(IIIII)V") \
+ METHOD (getState, "getState", "()I") \
+ METHOD (startRecording, "startRecording", "()V") \
+ METHOD (stop, "stop", "()V") \
+ METHOD (read, "read", "([SII)I") \
+ METHOD (release, "release", "()V") \
+
+DECLARE_JNI_CLASS (AudioRecord, "android/media/AudioRecord");
+#undef JNI_CLASS_MEMBERS
+
+//==============================================================================
+enum
+{
+ CHANNEL_OUT_STEREO = 12,
+ CHANNEL_IN_STEREO = 12,
+ CHANNEL_IN_MONO = 16,
+ ENCODING_PCM_16BIT = 2,
+ STREAM_MUSIC = 3,
+ MODE_STREAM = 1,
+ STATE_UNINITIALIZED = 0
+};
+
+const char* const javaAudioTypeName = "Android Audio";
+
+//==============================================================================
+class AndroidAudioIODevice : public AudioIODevice,
+ public Thread
+{
+public:
+ //==============================================================================
+ AndroidAudioIODevice (const String& deviceName)
+ : AudioIODevice (deviceName, javaAudioTypeName),
+ Thread ("audio"),
+ minBufferSizeOut (0), minBufferSizeIn (0), callback (0), sampleRate (0),
+ numClientInputChannels (0), numDeviceInputChannels (0), numDeviceInputChannelsAvailable (2),
+ numClientOutputChannels (0), numDeviceOutputChannels (0),
+ actualBufferSize (0), isRunning (false),
+ inputChannelBuffer (1, 1),
+ outputChannelBuffer (1, 1)
+ {
+ JNIEnv* env = getEnv();
+ sampleRate = env->CallStaticIntMethod (AudioTrack, AudioTrack.getNativeOutputSampleRate, MODE_STREAM);
+
+ minBufferSizeOut = (int) env->CallStaticIntMethod (AudioTrack, AudioTrack.getMinBufferSize, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT);
+ minBufferSizeIn = (int) env->CallStaticIntMethod (AudioRecord, AudioRecord.getMinBufferSize, sampleRate, CHANNEL_IN_STEREO, ENCODING_PCM_16BIT);
+
+ if (minBufferSizeIn <= 0)
+ {
+ minBufferSizeIn = env->CallStaticIntMethod (AudioRecord, AudioRecord.getMinBufferSize, sampleRate, CHANNEL_IN_MONO, ENCODING_PCM_16BIT);
+
+ if (minBufferSizeIn > 0)
+ numDeviceInputChannelsAvailable = 1;
+ else
+ numDeviceInputChannelsAvailable = 0;
+ }
+
+ DBG ("Audio device - min buffers: " << minBufferSizeOut << ", " << minBufferSizeIn << "; "
+ << sampleRate << " Hz; input chans: " << numDeviceInputChannelsAvailable);
+ }
+
+ ~AndroidAudioIODevice()
+ {
+ close();
+ }
+
+ StringArray getOutputChannelNames() override
+ {
+ StringArray s;
+ s.add ("Left");
+ s.add ("Right");
+ return s;
+ }
+
+ StringArray getInputChannelNames() override
+ {
+ StringArray s;
+
+ if (numDeviceInputChannelsAvailable == 2)
+ {
+ s.add ("Left");
+ s.add ("Right");
+ }
+ else if (numDeviceInputChannelsAvailable == 1)
+ {
+ s.add ("Audio Input");
+ }
+
+ return s;
+ }
+
+ Array getAvailableSampleRates() override
+ {
+ Array r;
+ r.add ((double) sampleRate);
+ return r;
+ }
+
+ Array getAvailableBufferSizes() override
+ {
+ Array b;
+ int n = 16;
+
+ for (int i = 0; i < 50; ++i)
+ {
+ b.add (n);
+ n += n < 64 ? 16
+ : (n < 512 ? 32
+ : (n < 1024 ? 64
+ : (n < 2048 ? 128 : 256)));
+ }
+
+ return b;
+ }
+
+ int getDefaultBufferSize() override { return 2048; }
+
+ String open (const BigInteger& inputChannels,
+ const BigInteger& outputChannels,
+ double requestedSampleRate,
+ int bufferSize) override
+ {
+ close();
+
+ if (sampleRate != (int) requestedSampleRate)
+ return "Sample rate not allowed";
+
+ lastError.clear();
+ int preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize;
+
+ numDeviceInputChannels = 0;
+ numDeviceOutputChannels = 0;
+
+ activeOutputChans = outputChannels;
+ activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false);
+ numClientOutputChannels = activeOutputChans.countNumberOfSetBits();
+
+ activeInputChans = inputChannels;
+ activeInputChans.setRange (2, activeInputChans.getHighestBit(), false);
+ numClientInputChannels = activeInputChans.countNumberOfSetBits();
+
+ actualBufferSize = preferredBufferSize;
+ inputChannelBuffer.setSize (2, actualBufferSize);
+ inputChannelBuffer.clear();
+ outputChannelBuffer.setSize (2, actualBufferSize);
+ outputChannelBuffer.clear();
+
+ JNIEnv* env = getEnv();
+
+ if (numClientOutputChannels > 0)
+ {
+ numDeviceOutputChannels = 2;
+ outputDevice = GlobalRef (env->NewObject (AudioTrack, AudioTrack.constructor,
+ STREAM_MUSIC, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT,
+ (jint) (minBufferSizeOut * numDeviceOutputChannels * sizeof (int16)), MODE_STREAM));
+
+ if (env->CallIntMethod (outputDevice, AudioTrack.getState) != STATE_UNINITIALIZED)
+ isRunning = true;
+ else
+ outputDevice.clear(); // failed to open the device
+ }
+
+ if (numClientInputChannels > 0 && numDeviceInputChannelsAvailable > 0)
+ {
+ numDeviceInputChannels = jmin (numClientInputChannels, numDeviceInputChannelsAvailable);
+ inputDevice = GlobalRef (env->NewObject (AudioRecord, AudioRecord.constructor,
+ 0 /* (default audio source) */, sampleRate,
+ numDeviceInputChannelsAvailable > 1 ? CHANNEL_IN_STEREO : CHANNEL_IN_MONO,
+ ENCODING_PCM_16BIT,
+ (jint) (minBufferSizeIn * numDeviceInputChannels * sizeof (int16))));
+
+ if (env->CallIntMethod (inputDevice, AudioRecord.getState) != STATE_UNINITIALIZED)
+ isRunning = true;
+ else
+ inputDevice.clear(); // failed to open the device
+ }
+
+ if (isRunning)
+ {
+ if (outputDevice != nullptr)
+ env->CallVoidMethod (outputDevice, AudioTrack.play);
+
+ if (inputDevice != nullptr)
+ env->CallVoidMethod (inputDevice, AudioRecord.startRecording);
+
+ startThread (8);
+ }
+ else
+ {
+ closeDevices();
+ }
+
+ return lastError;
+ }
+
+ void close() override
+ {
+ if (isRunning)
+ {
+ stopThread (2000);
+ isRunning = false;
+ closeDevices();
+ }
+ }
+
+ int getOutputLatencyInSamples() override { return (minBufferSizeOut * 3) / 4; }
+ int getInputLatencyInSamples() override { return (minBufferSizeIn * 3) / 4; }
+ bool isOpen() override { return isRunning; }
+ int getCurrentBufferSizeSamples() override { return actualBufferSize; }
+ int getCurrentBitDepth() override { return 16; }
+ double getCurrentSampleRate() override { return sampleRate; }
+ BigInteger getActiveOutputChannels() const override { return activeOutputChans; }
+ BigInteger getActiveInputChannels() const override { return activeInputChans; }
+ String getLastError() override { return lastError; }
+ bool isPlaying() override { return isRunning && callback != 0; }
+
+ void start (AudioIODeviceCallback* newCallback) override
+ {
+ if (isRunning && callback != newCallback)
+ {
+ if (newCallback != nullptr)
+ newCallback->audioDeviceAboutToStart (this);
+
+ const ScopedLock sl (callbackLock);
+ callback = newCallback;
+ }
+ }
+
+ void stop() override
+ {
+ if (isRunning)
+ {
+ AudioIODeviceCallback* lastCallback;
+
+ {
+ const ScopedLock sl (callbackLock);
+ lastCallback = callback;
+ callback = nullptr;
+ }
+
+ if (lastCallback != nullptr)
+ lastCallback->audioDeviceStopped();
+ }
+ }
+
+ void run() override
+ {
+ JNIEnv* env = getEnv();
+ jshortArray audioBuffer = env->NewShortArray (actualBufferSize * jmax (numDeviceOutputChannels, numDeviceInputChannels));
+
+ while (! threadShouldExit())
+ {
+ if (inputDevice != nullptr)
+ {
+ jint numRead = env->CallIntMethod (inputDevice, AudioRecord.read, audioBuffer, 0, actualBufferSize * numDeviceInputChannels);
+
+ if (numRead < actualBufferSize * numDeviceInputChannels)
+ {
+ DBG ("Audio read under-run! " << numRead);
+ }
+
+ jshort* const src = env->GetShortArrayElements (audioBuffer, 0);
+
+ for (int chan = 0; chan < inputChannelBuffer.getNumChannels(); ++chan)
+ {
+ AudioData::Pointer d (inputChannelBuffer.getWritePointer (chan));
+
+ if (chan < numDeviceInputChannels)
+ {
+ AudioData::Pointer s (src + chan, numDeviceInputChannels);
+ d.convertSamples (s, actualBufferSize);
+ }
+ else
+ {
+ d.clearSamples (actualBufferSize);
+ }
+ }
+
+ env->ReleaseShortArrayElements (audioBuffer, src, 0);
+ }
+
+ if (threadShouldExit())
+ break;
+
+ {
+ const ScopedLock sl (callbackLock);
+
+ if (callback != nullptr)
+ {
+ callback->audioDeviceIOCallback (inputChannelBuffer.getArrayOfReadPointers(), numClientInputChannels,
+ outputChannelBuffer.getArrayOfWritePointers(), numClientOutputChannels,
+ actualBufferSize);
+ }
+ else
+ {
+ outputChannelBuffer.clear();
+ }
+ }
+
+ if (outputDevice != nullptr)
+ {
+ if (threadShouldExit())
+ break;
+
+ jshort* const dest = env->GetShortArrayElements (audioBuffer, 0);
+
+ for (int chan = 0; chan < numDeviceOutputChannels; ++chan)
+ {
+ AudioData::Pointer d (dest + chan, numDeviceOutputChannels);
+
+ const float* const sourceChanData = outputChannelBuffer.getReadPointer (jmin (chan, outputChannelBuffer.getNumChannels() - 1));
+ AudioData::Pointer s (sourceChanData);
+ d.convertSamples (s, actualBufferSize);
+ }
+
+ env->ReleaseShortArrayElements (audioBuffer, dest, 0);
+ jint numWritten = env->CallIntMethod (outputDevice, AudioTrack.write, audioBuffer, 0, actualBufferSize * numDeviceOutputChannels);
+
+ if (numWritten < actualBufferSize * numDeviceOutputChannels)
+ {
+ DBG ("Audio write underrun! " << numWritten);
+ }
+ }
+ }
+ }
+
+ int minBufferSizeOut, minBufferSizeIn;
+
+private:
+ //==================================================================================================
+ CriticalSection callbackLock;
+ AudioIODeviceCallback* callback;
+ jint sampleRate;
+ int numClientInputChannels, numDeviceInputChannels, numDeviceInputChannelsAvailable;
+ int numClientOutputChannels, numDeviceOutputChannels;
+ int actualBufferSize;
+ bool isRunning;
+ String lastError;
+ BigInteger activeOutputChans, activeInputChans;
+ GlobalRef outputDevice, inputDevice;
+ AudioSampleBuffer inputChannelBuffer, outputChannelBuffer;
+
+ void closeDevices()
+ {
+ if (outputDevice != nullptr)
+ {
+ outputDevice.callVoidMethod (AudioTrack.stop);
+ outputDevice.callVoidMethod (AudioTrack.release);
+ outputDevice.clear();
+ }
+
+ if (inputDevice != nullptr)
+ {
+ inputDevice.callVoidMethod (AudioRecord.stop);
+ inputDevice.callVoidMethod (AudioRecord.release);
+ inputDevice.clear();
+ }
+ }
+
+ JUCE_DECLARE_NON_COPYABLE (AndroidAudioIODevice)
+};
+
+//==============================================================================
+class AndroidAudioIODeviceType : public AudioIODeviceType
+{
+public:
+ AndroidAudioIODeviceType() : AudioIODeviceType (javaAudioTypeName) {}
+
+ //==============================================================================
+ void scanForDevices() {}
+ StringArray getDeviceNames (bool wantInputNames) const { return StringArray (javaAudioTypeName); }
+ int getDefaultDeviceIndex (bool forInput) const { return 0; }
+ int getIndexOfDevice (AudioIODevice* device, bool asInput) const { return device != nullptr ? 0 : -1; }
+ bool hasSeparateInputsAndOutputs() const { return false; }
+
+ AudioIODevice* createDevice (const String& outputDeviceName,
+ const String& inputDeviceName)
+ {
+ ScopedPointer dev;
+
+ if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty())
+ {
+ dev = new AndroidAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName
+ : inputDeviceName);
+
+ if (dev->getCurrentSampleRate() <= 0 || dev->getDefaultBufferSize() <= 0)
+ dev = nullptr;
+ }
+
+ return dev.release();
+ }
+
+private:
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidAudioIODeviceType)
+};
+
+
+//==============================================================================
+extern bool isOpenSLAvailable();
+
+AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android()
+{
+ #if JUCE_USE_ANDROID_OPENSLES
+ if (isOpenSLAvailable())
+ return nullptr;
+ #endif
+
+ return new AndroidAudioIODeviceType();
+}
diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Midi.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Midi.cpp
new file mode 100644
index 0000000..88eeae6
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Midi.cpp
@@ -0,0 +1,85 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+StringArray MidiOutput::getDevices()
+{
+ StringArray devices;
+
+ return devices;
+}
+
+int MidiOutput::getDefaultDeviceIndex()
+{
+ return 0;
+}
+
+MidiOutput* MidiOutput::openDevice (int index)
+{
+ return nullptr;
+}
+
+MidiOutput::~MidiOutput()
+{
+ stopBackgroundThread();
+}
+
+void MidiOutput::sendMessageNow (const MidiMessage&)
+{
+}
+
+//==============================================================================
+MidiInput::MidiInput (const String& name_)
+ : name (name_),
+ internal (0)
+{
+}
+
+MidiInput::~MidiInput()
+{
+}
+
+void MidiInput::start()
+{
+}
+
+void MidiInput::stop()
+{
+}
+
+int MidiInput::getDefaultDeviceIndex()
+{
+ return 0;
+}
+
+StringArray MidiInput::getDevices()
+{
+ StringArray devs;
+
+ return devs;
+}
+
+MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback)
+{
+ return nullptr;
+}
diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp
new file mode 100644
index 0000000..53dc5df
--- /dev/null
+++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp
@@ -0,0 +1,632 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+
+ Permission is granted to use this software under the terms of either:
+ a) the GPL v2 (or any later version)
+ b) the Affero GPL v3
+
+ Details of these licenses can be found at: www.gnu.org/licenses
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.juce.com for more information.
+
+ ==============================================================================
+*/
+
+const char* const openSLTypeName = "Android OpenSL";
+
+bool isOpenSLAvailable()
+{
+ DynamicLibrary library;
+ return library.open ("libOpenSLES.so");
+}
+
+//==============================================================================
+class OpenSLAudioIODevice : public AudioIODevice,
+ public Thread
+{
+public:
+ OpenSLAudioIODevice (const String& deviceName)
+ : AudioIODevice (deviceName, openSLTypeName),
+ Thread ("OpenSL"),
+ callback (nullptr), sampleRate (0), deviceOpen (false),
+ inputBuffer (2, 2), outputBuffer (2, 2)
+ {
+ // OpenSL has piss-poor support for determining latency, so the only way I can find to
+ // get a number for this is by asking the AudioTrack/AudioRecord classes..
+ AndroidAudioIODevice javaDevice (String::empty);
+
+ // this is a total guess about how to calculate the latency, but seems to vaguely agree
+ // with the devices I've tested.. YMMV
+ inputLatency = ((javaDevice.minBufferSizeIn * 2) / 3);
+ outputLatency = ((javaDevice.minBufferSizeOut * 2) / 3);
+
+ const int longestLatency = jmax (inputLatency, outputLatency);
+ const int totalLatency = inputLatency + outputLatency;
+ inputLatency = ((longestLatency * inputLatency) / totalLatency) & ~15;
+ outputLatency = ((longestLatency * outputLatency) / totalLatency) & ~15;
+ }
+
+ ~OpenSLAudioIODevice()
+ {
+ close();
+ }
+
+ bool openedOk() const { return engine.outputMixObject != nullptr; }
+
+ StringArray getOutputChannelNames() override
+ {
+ StringArray s;
+ s.add ("Left");
+ s.add ("Right");
+ return s;
+ }
+
+ StringArray getInputChannelNames() override
+ {
+ StringArray s;
+ s.add ("Audio Input");
+ return s;
+ }
+
+ Array getAvailableSampleRates() override
+ {
+ static const double rates[] = { 8000.0, 16000.0, 32000.0, 44100.0, 48000.0 };
+ return Array (rates, numElementsInArray (rates));
+ }
+
+ Array getAvailableBufferSizes() override
+ {
+ static const int sizes[] = { 256, 512, 768, 1024, 1280, 1600 }; // must all be multiples of the block size
+ return Array (sizes, numElementsInArray (sizes));
+ }
+
+ String open (const BigInteger& inputChannels,
+ const BigInteger& outputChannels,
+ double requestedSampleRate,
+ int bufferSize) override
+ {
+ close();
+
+ lastError.clear();
+ sampleRate = (int) requestedSampleRate;
+
+ int preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize;
+
+ activeOutputChans = outputChannels;
+ activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false);
+ numOutputChannels = activeOutputChans.countNumberOfSetBits();
+
+ activeInputChans = inputChannels;
+ activeInputChans.setRange (1, activeInputChans.getHighestBit(), false);
+ numInputChannels = activeInputChans.countNumberOfSetBits();
+
+ actualBufferSize = preferredBufferSize;
+
+ inputBuffer.setSize (jmax (1, numInputChannels), actualBufferSize);
+ outputBuffer.setSize (jmax (1, numOutputChannels), actualBufferSize);
+ outputBuffer.clear();
+
+ recorder = engine.createRecorder (numInputChannels, sampleRate);
+ player = engine.createPlayer (numOutputChannels, sampleRate);
+
+ startThread (8);
+
+ deviceOpen = true;
+ return lastError;
+ }
+
+ void close() override
+ {
+ stop();
+ stopThread (6000);
+ deviceOpen = false;
+ recorder = nullptr;
+ player = nullptr;
+ }
+
+ int getDefaultBufferSize() override { return 1024; }
+ int getOutputLatencyInSamples() override { return outputLatency; }
+ int getInputLatencyInSamples() override { return inputLatency; }
+ bool isOpen() override { return deviceOpen; }
+ int getCurrentBufferSizeSamples() override { return actualBufferSize; }
+ int getCurrentBitDepth() override { return 16; }
+ double getCurrentSampleRate() override { return sampleRate; }
+ BigInteger getActiveOutputChannels() const override { return activeOutputChans; }
+ BigInteger getActiveInputChannels() const override { return activeInputChans; }
+ String getLastError() override { return lastError; }
+ bool isPlaying() override { return callback != nullptr; }
+
+ void start (AudioIODeviceCallback* newCallback) override
+ {
+ stop();
+
+ if (deviceOpen && callback != newCallback)
+ {
+ if (newCallback != nullptr)
+ newCallback->audioDeviceAboutToStart (this);
+
+ setCallback (newCallback);
+ }
+ }
+
+ void stop() override
+ {
+ if (AudioIODeviceCallback* const oldCallback = setCallback (nullptr))
+ 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
+ {
+ if (recorder != nullptr) recorder->start();
+ if (player != nullptr) player->start();
+
+ while (! threadShouldExit())
+ {
+ if (player != nullptr) player->writeBuffer (outputBuffer, *this);
+ if (recorder != nullptr) recorder->readNextBlock (inputBuffer, *this);
+
+ const ScopedLock sl (callbackLock);
+
+ if (callback != nullptr)
+ {
+ callback->audioDeviceIOCallback (numInputChannels > 0 ? inputBuffer.getArrayOfReadPointers() : nullptr, numInputChannels,
+ numOutputChannels > 0 ? outputBuffer.getArrayOfWritePointers() : nullptr, numOutputChannels,
+ actualBufferSize);
+ }
+ else
+ {
+ outputBuffer.clear();
+ }
+ }
+ }
+
+ //==================================================================================================
+ struct Engine
+ {
+ Engine()
+ : engineObject (nullptr), engineInterface (nullptr), outputMixObject (nullptr)
+ {
+ if (library.open ("libOpenSLES.so"))
+ {
+ typedef SLresult (*CreateEngineFunc) (SLObjectItf*, SLuint32, const SLEngineOption*, SLuint32, const SLInterfaceID*, const SLboolean*);
+
+ if (CreateEngineFunc createEngine = (CreateEngineFunc) library.getFunction ("slCreateEngine"))
+ {
+ check (createEngine (&engineObject, 0, nullptr, 0, nullptr, nullptr));
+
+ SLInterfaceID* SL_IID_ENGINE = (SLInterfaceID*) library.getFunction ("SL_IID_ENGINE");
+ SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (SLInterfaceID*) library.getFunction ("SL_IID_ANDROIDSIMPLEBUFFERQUEUE");
+ SL_IID_PLAY = (SLInterfaceID*) library.getFunction ("SL_IID_PLAY");
+ 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)->GetInterface (engineObject, *SL_IID_ENGINE, &engineInterface));
+
+ check ((*engineInterface)->CreateOutputMix (engineInterface, &outputMixObject, 0, nullptr, nullptr));
+ check ((*outputMixObject)->Realize (outputMixObject, SL_BOOLEAN_FALSE));
+ }
+ }
+ }
+
+ ~Engine()
+ {
+ if (outputMixObject != nullptr) (*outputMixObject)->Destroy (outputMixObject);
+ if (engineObject != nullptr) (*engineObject)->Destroy (engineObject);
+ }
+
+ Player* createPlayer (const int numChannels, const int sampleRate)
+ {
+ if (numChannels <= 0)
+ return nullptr;
+
+ ScopedPointer player (new Player (numChannels, sampleRate, *this));
+ return player->openedOk() ? player.release() : nullptr;
+ }
+
+ Recorder* createRecorder (const int numChannels, const int sampleRate)
+ {
+ if (numChannels <= 0)
+ return nullptr;
+
+ ScopedPointer recorder (new Recorder (numChannels, sampleRate, *this));
+ return recorder->openedOk() ? recorder.release() : nullptr;
+ }
+
+ SLObjectItf engineObject;
+ SLEngineItf engineInterface;
+ SLObjectItf outputMixObject;
+
+ SLInterfaceID* SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
+ SLInterfaceID* SL_IID_PLAY;
+ SLInterfaceID* SL_IID_RECORD;
+ SLInterfaceID* SL_IID_ANDROIDCONFIGURATION;
+
+ private:
+ DynamicLibrary library;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Engine)
+ };
+
+ //==================================================================================================
+ struct BufferList
+ {
+ BufferList (const int numChannels_)
+ : numChannels (numChannels_), bufferSpace (numChannels_ * numSamples * numBuffers), nextBlock (0)
+ {
+ }
+
+ int16* waitForFreeBuffer (Thread& threadToCheck)
+ {
+ while (numBlocksOut.get() == numBuffers)
+ {
+ dataArrived.wait (1);
+
+ if (threadToCheck.threadShouldExit())
+ return nullptr;
+ }
+
+ return getNextBuffer();
+ }
+
+ int16* getNextBuffer()
+ {
+ if (++nextBlock == numBuffers)
+ nextBlock = 0;
+
+ return bufferSpace + nextBlock * numChannels * numSamples;
+ }
+
+ void bufferReturned() { --numBlocksOut; dataArrived.signal(); }
+ void bufferSent() { ++numBlocksOut; dataArrived.signal(); }
+
+ int getBufferSizeBytes() const { return numChannels * numSamples * sizeof (int16); }
+
+ const int numChannels;
+ enum { numSamples = 256, numBuffers = 16 };
+
+ private:
+ HeapBlock bufferSpace;
+ int nextBlock;
+ Atomic numBlocksOut;
+ WaitableEvent dataArrived;
+ };
+
+ //==================================================================================================
+ struct Player
+ {
+ Player (int numChannels, int sampleRate, Engine& engine)
+ : playerObject (nullptr), playerPlay (nullptr), playerBufferQueue (nullptr),
+ bufferList (numChannels)
+ {
+ jassert (numChannels == 2);
+
+ SLDataFormat_PCM pcmFormat =
+ {
+ SL_DATAFORMAT_PCM,
+ (SLuint32) numChannels,
+ (SLuint32) (sampleRate * 1000), // (sample rate units are millihertz)
+ SL_PCMSAMPLEFORMAT_FIXED_16,
+ SL_PCMSAMPLEFORMAT_FIXED_16,
+ SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
+ SL_BYTEORDER_LITTLEENDIAN
+ };
+
+ SLDataLocator_AndroidSimpleBufferQueue bufferQueue = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, bufferList.numBuffers };
+ SLDataSource audioSrc = { &bufferQueue, &pcmFormat };
+
+ SLDataLocator_OutputMix outputMix = { SL_DATALOCATOR_OUTPUTMIX, engine.outputMixObject };
+ SLDataSink audioSink = { &outputMix, nullptr };
+
+ // (SL_IID_BUFFERQUEUE is not guaranteed to remain future-proof, so use SL_IID_ANDROIDSIMPLEBUFFERQUEUE)
+ const SLInterfaceID interfaceIDs[] = { *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
+ const SLboolean flags[] = { SL_BOOLEAN_TRUE };
+
+ check ((*engine.engineInterface)->CreateAudioPlayer (engine.engineInterface, &playerObject, &audioSrc, &audioSink,
+ 1, interfaceIDs, flags));
+
+ check ((*playerObject)->Realize (playerObject, SL_BOOLEAN_FALSE));
+ check ((*playerObject)->GetInterface (playerObject, *engine.SL_IID_PLAY, &playerPlay));
+ check ((*playerObject)->GetInterface (playerObject, *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &playerBufferQueue));
+ check ((*playerBufferQueue)->RegisterCallback (playerBufferQueue, staticCallback, this));
+ }
+
+ ~Player()
+ {
+ if (playerPlay != nullptr)
+ check ((*playerPlay)->SetPlayState (playerPlay, SL_PLAYSTATE_STOPPED));
+
+ if (playerBufferQueue != nullptr)
+ check ((*playerBufferQueue)->Clear (playerBufferQueue));
+
+ if (playerObject != nullptr)
+ (*playerObject)->Destroy (playerObject);
+ }
+
+ bool openedOk() const noexcept { return playerBufferQueue != nullptr; }
+
+ void start()
+ {
+ jassert (openedOk());
+ check ((*playerPlay)->SetPlayState (playerPlay, SL_PLAYSTATE_PLAYING));
+ }
+
+ void writeBuffer (const AudioSampleBuffer& buffer, Thread& thread)
+ {
+ jassert (buffer.getNumChannels() == bufferList.numChannels);
+ jassert (buffer.getNumSamples() < bufferList.numSamples * bufferList.numBuffers);
+
+ int offset = 0;
+ int numSamples = buffer.getNumSamples();
+
+ while (numSamples > 0)
+ {
+ int16* const destBuffer = bufferList.waitForFreeBuffer (thread);
+
+ if (destBuffer == nullptr)
+ break;
+
+ for (int i = 0; i < bufferList.numChannels; ++i)
+ {
+ typedef AudioData::Pointer DstSampleType;
+ typedef AudioData::Pointer