diff --git a/source/backend/CarlaHostCommon.cpp b/source/backend/CarlaHostCommon.cpp index 405ce4e38..d72d9ec6d 100644 --- a/source/backend/CarlaHostCommon.cpp +++ b/source/backend/CarlaHostCommon.cpp @@ -18,7 +18,7 @@ #include "CarlaHost.h" #include "CarlaString.hpp" -#include "juce_core.h" +#include "juce_core/juce_core.h" namespace CB = CarlaBackend; diff --git a/source/backend/CarlaStandalone.cpp b/source/backend/CarlaStandalone.cpp index 7ae0f6723..1aa694fa7 100644 --- a/source/backend/CarlaStandalone.cpp +++ b/source/backend/CarlaStandalone.cpp @@ -27,10 +27,10 @@ #include "CarlaBackendUtils.hpp" #include "CarlaBase64Utils.hpp" -#include "juce_audio_formats.h" +#include "juce_audio_formats/juce_audio_formats.h" #if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN) -# include "juce_gui_basics.h" +# include "juce_gui_basics/juce_gui_basics.h" #else namespace juce { # include "juce_events/messages/juce_Initialisation.h" diff --git a/source/backend/CarlaStandaloneNSM.cpp b/source/backend/CarlaStandaloneNSM.cpp index 4374689d6..8e142509f 100644 --- a/source/backend/CarlaStandaloneNSM.cpp +++ b/source/backend/CarlaStandaloneNSM.cpp @@ -28,7 +28,7 @@ #include "CarlaHost.h" #include "CarlaOscUtils.hpp" #include "CarlaString.hpp" -#include "juce_core.h" +#include "juce_core/juce_core.h" namespace CB = CarlaBackend; diff --git a/source/backend/CarlaUtils.cpp b/source/backend/CarlaUtils.cpp index 015ac26be..6e21dad62 100644 --- a/source/backend/CarlaUtils.cpp +++ b/source/backend/CarlaUtils.cpp @@ -25,10 +25,10 @@ #include "CarlaThread.hpp" #include "LinkedList.hpp" -#include "juce_audio_formats.h" +#include "juce_audio_formats/juce_audio_formats.h" #ifdef CARLA_OS_MAC -# include "juce_audio_processors.h" +# include "juce_audio_processors/juce_audio_processors.h" #endif #include "../native-plugins/_data.cpp" diff --git a/source/backend/engine/CarlaEngine.cpp b/source/backend/engine/CarlaEngine.cpp index dca373a21..44fa6a6a5 100644 --- a/source/backend/engine/CarlaEngine.cpp +++ b/source/backend/engine/CarlaEngine.cpp @@ -35,7 +35,7 @@ #include "CarlaMIDI.h" #include "jackbridge/JackBridge.hpp" -#include "juce_core.h" +#include "juce_core/juce_core.h" using juce::CharPointer_UTF8; using juce::File; diff --git a/source/backend/engine/CarlaEngineGraph.cpp b/source/backend/engine/CarlaEngineGraph.cpp index 827519e37..b96277563 100644 --- a/source/backend/engine/CarlaEngineGraph.cpp +++ b/source/backend/engine/CarlaEngineGraph.cpp @@ -22,6 +22,15 @@ #include "CarlaMathUtils.hpp" #include "CarlaMIDI.h" +// FIXME: update to new Juce API +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-declarations" +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + using juce::AudioPluginInstance; using juce::AudioProcessor; using juce::AudioProcessorEditor; @@ -1045,12 +1054,12 @@ const String getProcessorFullPortName(AudioProcessor* const proc, const uint32_t } else if (portId >= kAudioOutputPortOffset) { - CARLA_SAFE_ASSERT_RETURN(proc->getNumOutputChannels() > 0, String()); + CARLA_SAFE_ASSERT_RETURN(proc->getTotalNumOutputChannels() > 0, String()); fullPortName += ":" + proc->getOutputChannelName(static_cast(portId-kAudioOutputPortOffset)); } else if (portId >= kAudioInputPortOffset) { - CARLA_SAFE_ASSERT_RETURN(proc->getNumInputChannels() > 0, String()); + CARLA_SAFE_ASSERT_RETURN(proc->getTotalNumInputChannels() > 0, String()); fullPortName += ":" + proc->getInputChannelName(static_cast(portId-kAudioInputPortOffset)); } else @@ -1070,13 +1079,13 @@ void addNodeToPatchbay(CarlaEngine* const engine, const uint32_t groupId, const const int icon((clientId >= 0) ? PATCHBAY_ICON_PLUGIN : PATCHBAY_ICON_HARDWARE); engine->callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, groupId, icon, clientId, 0.0f, proc->getName().toRawUTF8()); - for (int i=0, numInputs=proc->getNumInputChannels(); igetTotalNumInputChannels(); icallback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, groupId, static_cast(kAudioInputPortOffset)+i, PATCHBAY_PORT_TYPE_AUDIO|PATCHBAY_PORT_IS_INPUT, 0.0f, proc->getInputChannelName(i).toRawUTF8()); } - for (int i=0, numOutputs=proc->getNumOutputChannels(); igetTotalNumOutputChannels(); icallback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, groupId, static_cast(kAudioOutputPortOffset)+i, PATCHBAY_PORT_TYPE_AUDIO, 0.0f, proc->getOutputChannelName(i).toRawUTF8()); @@ -1101,13 +1110,13 @@ void removeNodeFromPatchbay(CarlaEngine* const engine, const uint32_t groupId, c CARLA_SAFE_ASSERT_RETURN(engine != nullptr,); CARLA_SAFE_ASSERT_RETURN(proc != nullptr,); - for (int i=0, numInputs=proc->getNumInputChannels(); igetTotalNumInputChannels(); icallback(ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED, groupId, static_cast(kAudioInputPortOffset)+i, 0, 0.0f, nullptr); } - for (int i=0, numOutputs=proc->getNumOutputChannels(); igetTotalNumOutputChannels(); icallback(ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED, groupId, static_cast(kAudioOutputPortOffset)+i, 0, 0.0f, nullptr); @@ -1799,7 +1808,7 @@ bool PatchbayGraph::getGroupAndPortIdFromFullName(const bool external, const cha return true; } - for (int j=0, numInputs=proc->getNumInputChannels(); jgetTotalNumInputChannels(); jgetInputChannelName(j) != portName) continue; @@ -1808,7 +1817,7 @@ bool PatchbayGraph::getGroupAndPortIdFromFullName(const bool external, const cha return true; } - for (int j=0, numOutputs=proc->getNumOutputChannels(); jgetTotalNumOutputChannels(); jgetOutputChannelName(j) != portName) continue; @@ -2252,4 +2261,11 @@ bool CarlaEngine::disconnectExternalGraphPort(const uint connectionType, const u CARLA_BACKEND_END_NAMESPACE +// enable -Wdeprecated-declarations again +#if defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +# pragma GCC diagnostic pop +#endif + // ----------------------------------------------------------------------- diff --git a/source/backend/engine/CarlaEngineGraph.hpp b/source/backend/engine/CarlaEngineGraph.hpp index be7ee1ad0..aa4b9de7b 100644 --- a/source/backend/engine/CarlaEngineGraph.hpp +++ b/source/backend/engine/CarlaEngineGraph.hpp @@ -23,7 +23,8 @@ #include "CarlaPatchbayUtils.hpp" #include "CarlaStringList.hpp" -#include "juce_audio_processors.h" +#include "juce_audio_processors/juce_audio_processors.h" + using juce::AudioProcessorGraph; using juce::AudioSampleBuffer; using juce::MidiBuffer; diff --git a/source/backend/engine/CarlaEngineJack.cpp b/source/backend/engine/CarlaEngineJack.cpp index 21111a216..47b93fb12 100644 --- a/source/backend/engine/CarlaEngineJack.cpp +++ b/source/backend/engine/CarlaEngineJack.cpp @@ -26,7 +26,7 @@ #include "CarlaStringList.hpp" #include "jackey.h" -#include "juce_audio_basics.h" +#include "juce_audio_basics/juce_audio_basics.h" #ifdef __SSE2_MATH__ # include diff --git a/source/backend/engine/CarlaEngineNative.cpp b/source/backend/engine/CarlaEngineNative.cpp index 65e0fe201..3fdfbecb5 100644 --- a/source/backend/engine/CarlaEngineNative.cpp +++ b/source/backend/engine/CarlaEngineNative.cpp @@ -34,10 +34,10 @@ #include "CarlaHost.h" #include "CarlaNative.hpp" -#include "juce_audio_basics.h" +#include "juce_audio_basics/juce_audio_basics.h" #if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN) -# include "juce_gui_basics.h" +# include "juce_gui_basics/juce_gui_basics.h" #else namespace juce { # include "juce_events/messages/juce_Initialisation.h" diff --git a/source/backend/engine/CarlaEngineRtAudio.cpp b/source/backend/engine/CarlaEngineRtAudio.cpp index 380e5b3b2..d8ce4c250 100644 --- a/source/backend/engine/CarlaEngineRtAudio.cpp +++ b/source/backend/engine/CarlaEngineRtAudio.cpp @@ -24,7 +24,7 @@ #include "RtLinkedList.hpp" #include "jackbridge/JackBridge.hpp" -#include "juce_audio_basics.h" +#include "juce_audio_basics/juce_audio_basics.h" #include "rtaudio/RtAudio.h" #include "rtmidi/RtMidi.h" diff --git a/source/backend/plugin/CarlaPlugin.cpp b/source/backend/plugin/CarlaPlugin.cpp index 94c22cf4a..b8161f973 100644 --- a/source/backend/plugin/CarlaPlugin.cpp +++ b/source/backend/plugin/CarlaPlugin.cpp @@ -25,7 +25,7 @@ #include -#include "juce_core.h" +#include "juce_core/juce_core.h" using juce::CharPointer_UTF8; using juce::File; diff --git a/source/backend/plugin/CarlaPluginFluidSynth.cpp b/source/backend/plugin/CarlaPluginFluidSynth.cpp index 6da5c9007..595e22a77 100644 --- a/source/backend/plugin/CarlaPluginFluidSynth.cpp +++ b/source/backend/plugin/CarlaPluginFluidSynth.cpp @@ -22,7 +22,7 @@ #include "CarlaMathUtils.hpp" -#include "juce_core.h" +#include "juce_core/juce_core.h" #include diff --git a/source/backend/plugin/CarlaPluginInternal.hpp b/source/backend/plugin/CarlaPluginInternal.hpp index 707550a72..2112fb145 100644 --- a/source/backend/plugin/CarlaPluginInternal.hpp +++ b/source/backend/plugin/CarlaPluginInternal.hpp @@ -28,7 +28,7 @@ #include "CarlaString.hpp" #include "RtLinkedList.hpp" -#include "juce_audio_basics.h" +#include "juce_audio_basics/juce_audio_basics.h" using juce::FloatVectorOperations; diff --git a/source/backend/plugin/CarlaPluginJuce.cpp b/source/backend/plugin/CarlaPluginJuce.cpp index cf774738c..e8b83e902 100644 --- a/source/backend/plugin/CarlaPluginJuce.cpp +++ b/source/backend/plugin/CarlaPluginJuce.cpp @@ -24,7 +24,7 @@ #include "CarlaMathUtils.hpp" #include "JucePluginWindow.hpp" -#include "juce_audio_processors.h" +#include "juce_audio_processors/juce_audio_processors.h" using namespace juce; @@ -359,9 +359,9 @@ public: bool needsCtrlIn, needsCtrlOut; needsCtrlIn = needsCtrlOut = false; - aIns = (fInstance->getNumInputChannels() > 0) ? static_cast(fInstance->getNumInputChannels()) : 0; - aOuts = (fInstance->getNumOutputChannels() > 0) ? static_cast(fInstance->getNumOutputChannels()) : 0; - params = (fInstance->getNumParameters() > 0) ? static_cast(fInstance->getNumParameters()) : 0; + aIns = (fInstance->getTotalNumInputChannels() > 0) ? static_cast(fInstance->getTotalNumInputChannels()) : 0; + aOuts = (fInstance->getTotalNumOutputChannels() > 0) ? static_cast(fInstance->getTotalNumOutputChannels()) : 0; + params = (fInstance->getNumParameters() > 0) ? static_cast(fInstance->getNumParameters()) : 0; if (fInstance->acceptsMidi()) { diff --git a/source/backend/plugin/CarlaPluginLV2.cpp b/source/backend/plugin/CarlaPluginLV2.cpp index 1d292fdb2..81c78c004 100644 --- a/source/backend/plugin/CarlaPluginLV2.cpp +++ b/source/backend/plugin/CarlaPluginLV2.cpp @@ -37,7 +37,7 @@ extern "C" { #include "rtmempool/rtmempool-lv2.h" } -#include "juce_core.h" +#include "juce_core/juce_core.h" using juce::File; diff --git a/source/backend/plugin/CarlaPluginLinuxSampler.cpp b/source/backend/plugin/CarlaPluginLinuxSampler.cpp index 51c526f43..829d16fc4 100644 --- a/source/backend/plugin/CarlaPluginLinuxSampler.cpp +++ b/source/backend/plugin/CarlaPluginLinuxSampler.cpp @@ -31,7 +31,7 @@ #include "CarlaBackendUtils.hpp" #include "CarlaMathUtils.hpp" -#include "juce_core.h" +#include "juce_core/juce_core.h" #include diff --git a/source/backend/plugin/CarlaPluginNative.cpp b/source/backend/plugin/CarlaPluginNative.cpp index df6ca79d9..3d2f34913 100644 --- a/source/backend/plugin/CarlaPluginNative.cpp +++ b/source/backend/plugin/CarlaPluginNative.cpp @@ -21,7 +21,7 @@ #include "CarlaMathUtils.hpp" #include "CarlaNative.h" -#include "juce_core.h" +#include "juce_core/juce_core.h" using juce::jmax; using juce::String; diff --git a/source/backend/plugin/CarlaPluginVST2.cpp b/source/backend/plugin/CarlaPluginVST2.cpp index c8ec524c3..7db882b6f 100644 --- a/source/backend/plugin/CarlaPluginVST2.cpp +++ b/source/backend/plugin/CarlaPluginVST2.cpp @@ -29,7 +29,7 @@ #include "CarlaMathUtils.hpp" #include "CarlaPluginUI.hpp" -#include "juce_core.h" +#include "juce_core/juce_core.h" #include diff --git a/source/bridges-plugin/CarlaBridgePlugin.cpp b/source/bridges-plugin/CarlaBridgePlugin.cpp index fbbee2fea..7f033defe 100644 --- a/source/bridges-plugin/CarlaBridgePlugin.cpp +++ b/source/bridges-plugin/CarlaBridgePlugin.cpp @@ -30,10 +30,10 @@ #endif #include "jackbridge/JackBridge.hpp" -#include "juce_core.h" +#include "juce_core/juce_core.h" #if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN) -# include "juce_gui_basics.h" +# include "juce_gui_basics/juce_gui_basics.h" using juce::JUCEApplication; using juce::JUCEApplicationBase; using juce::Timer; diff --git a/source/bridges-ui/CarlaBridgeUI-LV2.cpp b/source/bridges-ui/CarlaBridgeUI-LV2.cpp index b24a5e75d..5524d507f 100644 --- a/source/bridges-ui/CarlaBridgeUI-LV2.cpp +++ b/source/bridges-ui/CarlaBridgeUI-LV2.cpp @@ -21,7 +21,7 @@ #include "CarlaMIDI.h" #include "LinkedList.hpp" -#include "juce_core.h" +#include "juce_core/juce_core.h" #define URI_CARLA_ATOM_WORKER "http://kxstudio.sf.net/ns/carla/atomWorker" diff --git a/source/discovery/carla-discovery.cpp b/source/discovery/carla-discovery.cpp index 891eba405..85fbb3321 100644 --- a/source/discovery/carla-discovery.cpp +++ b/source/discovery/carla-discovery.cpp @@ -23,7 +23,7 @@ #if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN) # define USE_JUCE_PROCESSORS -# include "juce_audio_processors.h" +# include "juce_audio_processors/juce_audio_processors.h" #endif #ifdef BUILD_BRIDGE @@ -46,14 +46,15 @@ #include -#include "juce_core.h" +#include "juce_core/juce_core.h" + +#define DISCOVERY_OUT(x, y) std::cout << "\ncarla-discovery::" << x << "::" << y << std::endl; + using juce::CharPointer_UTF8; using juce::File; using juce::String; using juce::StringArray; -#define DISCOVERY_OUT(x, y) std::cout << "\ncarla-discovery::" << x << "::" << y << std::endl; - CARLA_BACKEND_USE_NAMESPACE // -------------------------------------------------------------------------- diff --git a/source/modules/AppConfig.h b/source/modules/AppConfig.h new file mode 100644 index 000000000..7f1cd4c67 --- /dev/null +++ b/source/modules/AppConfig.h @@ -0,0 +1,391 @@ + +#ifndef CARLA_JUCE_APPCONFIG_H_INCLUDED +#define CARLA_JUCE_APPCONFIG_H_INCLUDED + +// -------------------------------------------------------------------------------------------------------------------- +// Check OS + +#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) +# define APPCONFIG_OS_WIN64 +#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +# define APPCONFIG_OS_WIN32 +#elif defined(__APPLE__) +# define APPCONFIG_OS_MAC +#elif defined(__HAIKU__) +# define APPCONFIG_OS_HAIKU +#elif defined(__linux__) || defined(__linux) +# define APPCONFIG_OS_LINUX +#else +# warning Unsupported platform! +#endif + +#if defined(APPCONFIG_OS_WIN32) || defined(APPCONFIG_OS_WIN64) +# define APPCONFIG_OS_WIN +#elif defined(APPCONFIG_OS_LINUX) || defined(APPCONFIG_OS_MAC) +# define APPCONFIG_OS_UNIX +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +// always enabled +#define JUCE_MODULE_AVAILABLE_juce_audio_basics 1 +#define JUCE_MODULE_AVAILABLE_juce_audio_formats 1 +#define JUCE_MODULE_AVAILABLE_juce_core 1 + +// always disabled +#define JUCE_MODULE_AVAILABLE_juce_audio_plugin_client 0 +#define JUCE_MODULE_AVAILABLE_juce_audio_utils 0 +#define JUCE_MODULE_AVAILABLE_juce_cryptography 0 +#define JUCE_MODULE_AVAILABLE_juce_opengl 0 +#define JUCE_MODULE_AVAILABLE_juce_video 0 + +// conditional +#if defined(APPCONFIG_OS_MAC) || defined(APPCONFIG_OS_WIN) +# define JUCE_MODULE_AVAILABLE_juce_audio_devices 1 +# define JUCE_MODULE_AVAILABLE_juce_audio_processors 1 +# define JUCE_MODULE_AVAILABLE_juce_data_structures 1 +# define JUCE_MODULE_AVAILABLE_juce_events 1 +# define JUCE_MODULE_AVAILABLE_juce_graphics 1 +# define JUCE_MODULE_AVAILABLE_juce_gui_basics 1 +# define JUCE_MODULE_AVAILABLE_juce_gui_extra 1 +#else +# define JUCE_MODULE_AVAILABLE_juce_audio_devices 0 +# define JUCE_MODULE_AVAILABLE_juce_audio_processors 0 +# define JUCE_MODULE_AVAILABLE_juce_data_structures 0 +# define JUCE_MODULE_AVAILABLE_juce_events 0 +# define JUCE_MODULE_AVAILABLE_juce_graphics 0 +# define JUCE_MODULE_AVAILABLE_juce_gui_basics 0 +# define JUCE_MODULE_AVAILABLE_juce_gui_extra 0 +#endif + +// misc +#define JUCE_DISABLE_JUCE_VERSION_PRINTING 1 +#define JUCE_STANDALONE_APPLICATION 0 +#define JUCE_STRING_UTF_TYPE 8 +#define JUCE_USE_VFORK 1 + +#if ! (defined(APPCONFIG_OS_MAC) || defined(APPCONFIG_OS_WIN)) +# define JUCE_MODAL_LOOPS_PERMITTED 0 +# define JUCE_AUDIO_PROCESSOR_NO_GUI 1 +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// juce_audio_basics + +// nothing here + +// -------------------------------------------------------------------------------------------------------------------- +// juce_audio_devices + +//============================================================================= +/** 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. +*/ +#ifdef APPCONFIG_OS_WIN + #define JUCE_ASIO 1 +#else + #define JUCE_ASIO 0 +#endif + +/** Config: JUCE_WASAPI + Enables WASAPI audio devices (Windows Vista and above). +*/ +#define JUCE_WASAPI 0 + +/** Config: JUCE_DIRECTSOUND + Enables DirectSound audio (MS Windows only). +*/ +#ifdef APPCONFIG_OS_WIN + #define JUCE_DIRECTSOUND 1 +#else + #define JUCE_DIRECTSOUND 0 +#endif + +/** Config: JUCE_ALSA + Enables ALSA audio devices (Linux only). +*/ +#if 0 //APPCONFIG_OS_LINUX + #define JUCE_ALSA 1 + #define JUCE_ALSA_MIDI_INPUT_NAME "Carla" + #define JUCE_ALSA_MIDI_OUTPUT_NAME "Carla" + #define JUCE_ALSA_MIDI_INPUT_PORT_NAME "Midi In" + #define JUCE_ALSA_MIDI_OUTPUT_PORT_NAME "Midi Out" +#else + #define JUCE_ALSA 0 +#endif + +/** Config: JUCE_JACK + Enables JACK audio devices (Linux only). +*/ +#if 0 //APPCONFIG_OS_LINUX + #define JUCE_JACK 1 + #define JUCE_JACK_CLIENT_NAME "Carla" +#else + #define JUCE_JACK 0 +#endif + +//============================================================================= +/** Config: JUCE_USE_CDREADER + Enables the AudioCDReader class (on supported platforms). +*/ +#define JUCE_USE_CDREADER 0 + +/** Config: JUCE_USE_CDBURNER + Enables the AudioCDBurner class (on supported platforms). +*/ +#define JUCE_USE_CDBURNER 0 + +// -------------------------------------------------------------------------------------------------------------------- +// juce_audio_formats + +//============================================================================= +/** Config: JUCE_USE_FLAC + Enables the FLAC audio codec classes (available on all platforms). + If your app doesn't need to read FLAC files, you might want to disable this to + reduce the size of your codebase and build time. +*/ +#define JUCE_USE_FLAC 1 + +/** Config: JUCE_USE_OGGVORBIS + Enables the Ogg-Vorbis audio codec classes (available on all platforms). + If your app doesn't need to read Ogg-Vorbis files, you might want to disable this to + reduce the size of your codebase and build time. +*/ +#define JUCE_USE_OGGVORBIS 1 + +/** Config: JUCE_USE_MP3AUDIOFORMAT + Enables the software-based MP3AudioFormat class. + IMPORTANT DISCLAIMER: By choosing to enable the JUCE_USE_MP3AUDIOFORMAT flag and to compile + this MP3 code into your software, you do so AT YOUR OWN RISK! By doing so, you are agreeing + that Raw Material Software is in no way responsible for any patent, copyright, or other + legal issues that you may suffer as a result. + + The code in juce_MP3AudioFormat.cpp is NOT guaranteed to be free from infringements of 3rd-party + intellectual property. If you wish to use it, please seek your own independent advice about the + legality of doing so. If you are not willing to accept full responsibility for the consequences + of using this code, then do not enable this setting. +*/ +#define JUCE_USE_MP3AUDIOFORMAT 0 + +/** Config: JUCE_USE_LAME_AUDIO_FORMAT + Enables the LameEncoderAudioFormat class. +*/ +#define JUCE_USE_LAME_AUDIO_FORMAT 1 + +/** Config: JUCE_USE_WINDOWS_MEDIA_FORMAT + Enables the Windows Media SDK codecs. +*/ +#define JUCE_USE_WINDOWS_MEDIA_FORMAT 0 + +// -------------------------------------------------------------------------------------------------------------------- +// juce_audio_processors + +//============================================================================= +/** Config: JUCE_PLUGINHOST_VST + Enables the VST audio plugin hosting classes. This requires the Steinberg VST SDK to be + installed on your machine. + + @see VSTPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU +*/ +#ifndef VESTIGE_HEADER +# define JUCE_PLUGINHOST_VST 1 +#else +# define JUCE_PLUGINHOST_VST 0 +#endif + +/** Config: JUCE_PLUGINHOST_VST3 + Enables the VST3 audio plugin hosting classes. This requires the Steinberg VST3 SDK to be + installed on your machine. + + @see VSTPluginFormat, VST3PluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST, JUCE_PLUGINHOST_AU +*/ +#if defined(APPCONFIG_OS_MAC) || defined(APPCONFIG_OS_WIN) +# define JUCE_PLUGINHOST_VST3 1 +#else +# define JUCE_PLUGINHOST_VST3 0 +#endif + +/** Config: JUCE_PLUGINHOST_AU + Enables the AudioUnit plugin hosting classes. This is Mac-only, of course. + + @see AudioUnitPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST +*/ +#ifdef APPCONFIG_OS_MAC +# define JUCE_PLUGINHOST_AU 1 +#else +# define JUCE_PLUGINHOST_AU 0 +#endif + +#define JUCE_PLUGINHOST_LADSPA 0 + +// -------------------------------------------------------------------------------------------------------------------- +// juce_core + +//============================================================================= +/** Config: JUCE_FORCE_DEBUG + + Normally, JUCE_DEBUG is set to 1 or 0 based on compiler and project settings, + but if you define this value, you can override this to force it to be true or false. +*/ +#define JUCE_FORCE_DEBUG 0 + +//============================================================================= +/** Config: JUCE_LOG_ASSERTIONS + + If this flag is enabled, the the jassert and jassertfalse macros will always use Logger::writeToLog() + to write a message when an assertion happens. + + Enabling it will also leave this turned on in release builds. When it's disabled, + however, the jassert and jassertfalse macros will not be compiled in a + release build. + + @see jassert, jassertfalse, Logger +*/ +#define JUCE_LOG_ASSERTIONS 1 + +//============================================================================= +/** Config: JUCE_CHECK_MEMORY_LEAKS + + Enables a memory-leak check for certain objects when the app terminates. See the LeakedObjectDetector + class and the JUCE_LEAK_DETECTOR macro for more details about enabling leak checking for specific classes. +*/ +#ifdef DEBUG + #define JUCE_CHECK_MEMORY_LEAKS 1 +#else + #define JUCE_CHECK_MEMORY_LEAKS 0 +#endif + +//============================================================================= +/** Config: JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES + + In a Visual C++ build, this can be used to stop the required system libs being + automatically added to the link stage. +*/ +#define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES 0 + +/** Config: JUCE_INCLUDE_ZLIB_CODE + This can be used to disable Juce's embedded 3rd-party zlib code. + You might need to tweak this if you're linking to an external zlib library in your app, + but for normal apps, this option should be left alone. + + If you disable this, you might also want to set a value for JUCE_ZLIB_INCLUDE_PATH, to + specify the path where your zlib headers live. +*/ +#define JUCE_INCLUDE_ZLIB_CODE 1 + +/** Config: JUCE_USE_CURL + Enables http/https support via libcurl (Linux only). Enabling this will add an additional + run-time dynmic dependency to libcurl. + + If you disable this then https/ssl support will not be available on linux. +*/ +#define JUCE_USE_CURL 0 + +/* Config: JUCE_CATCH_UNHANDLED_EXCEPTIONS + If enabled, this will add some exception-catching code to forward unhandled exceptions + to your JUCEApplicationBase::unhandledException() callback. +*/ +#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 0 + +// -------------------------------------------------------------------------------------------------------------------- +// juce_data_structures + +// nothing here + +// -------------------------------------------------------------------------------------------------------------------- +// juce_events + +// nothing here + +// -------------------------------------------------------------------------------------------------------------------- +// juce_graphics + +//============================================================================= +/** Config: JUCE_USE_COREIMAGE_LOADER + + On OSX, enabling this flag means that the CoreImage codecs will be used to load + PNG/JPEG/GIF files. It is enabled by default, but you may want to disable it if + you'd rather use libpng, libjpeg, etc. +*/ +#define JUCE_USE_COREIMAGE_LOADER 1 + +/** Config: JUCE_USE_DIRECTWRITE + + Enabling this flag means that DirectWrite will be used when available for font + management and layout. +*/ +#define JUCE_USE_DIRECTWRITE 1 + +#define JUCE_INCLUDE_PNGLIB_CODE 1 + +#define JUCE_INCLUDE_JPEGLIB_CODE 1 + +#define USE_COREGRAPHICS_RENDERING 1 + +// -------------------------------------------------------------------------------------------------------------------- +// juce_gui_basics + +//============================================================================= +/** Config: JUCE_ENABLE_REPAINT_DEBUGGING + If this option is turned on, each area of the screen that gets repainted will + flash in a random colour, so that you can see exactly which bits of your + components are being drawn. +*/ +#define JUCE_ENABLE_REPAINT_DEBUGGING 0 + +/** JUCE_USE_XRANDR: Enables Xrandr multi-monitor support (Linux only). + Unless you specifically want to disable this, it's best to leave this option turned on. + Note that your users do not need to have Xrandr installed for your JUCE app to run, as + the availability of Xrandr is queried during runtime. +*/ +#define JUCE_USE_XRANDR 0 + +/** JUCE_USE_XINERAMA: Enables Xinerama multi-monitor support (Linux only). + Unless you specifically want to disable this, it's best to leave this option turned on. + This will be used as a fallback if JUCE_USE_XRANDR not set or libxrandr cannot be found. + Note that your users do not need to have Xrandr installed for your JUCE app to run, as + the availability of Xinerama is queried during runtime. +*/ +#define JUCE_USE_XINERAMA 0 + +/** Config: JUCE_USE_XSHM + Enables X shared memory for faster rendering on Linux. This is best left turned on + unless you have a good reason to disable it. +*/ +#define JUCE_USE_XSHM 1 + +/** Config: JUCE_USE_XRENDER + Enables XRender to allow semi-transparent windowing on Linux. +*/ +#define JUCE_USE_XRENDER 0 + +/** Config: JUCE_USE_XCURSOR + Uses XCursor to allow ARGB cursor on Linux. This is best left turned on unless you have + a good reason to disable it. +*/ +#define JUCE_USE_XCURSOR 1 + +// -------------------------------------------------------------------------------------------------------------------- +// juce_gui_extra + +//============================================================================= +/** Config: JUCE_WEB_BROWSER + This lets you disable the WebBrowserComponent class (Mac and Windows). + If you're not using any embedded web-pages, turning this off may reduce your code size. +*/ +#define JUCE_WEB_BROWSER 0 + +/** Config: JUCE_ENABLE_LIVE_CONSTANT_EDITOR + This lets you turn on the JUCE_ENABLE_LIVE_CONSTANT_EDITOR support. See the documentation + for that macro for more details. +*/ +#define JUCE_ENABLE_LIVE_CONSTANT_EDITOR 0 + +// -------------------------------------------------------------------------------------------------------------------- + +#endif // CARLA_JUCE_APPCONFIG_H_INCLUDED diff --git a/source/modules/juce_audio_basics.h b/source/modules/juce_audio_basics.h deleted file mode 100644 index 032e9e8b2..000000000 --- a/source/modules/juce_audio_basics.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Carla Juce setup - * Copyright (C) 2013-2014 Filipe Coelho - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or any later version. - * - * This program 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. - * - * For a full copy of the GNU General Public License see the doc/GPL.txt file. - */ - -#ifndef CARLA_JUCE_AUDIO_BASICS_H_INCLUDED -#define CARLA_JUCE_AUDIO_BASICS_H_INCLUDED - -#include "juce_core.h" - -#include "juce_audio_basics/AppConfig.h" -#include "juce_audio_basics/juce_audio_basics.h" - -#endif // CARLA_JUCE_AUDIO_BASICS_H_INCLUDED diff --git a/source/modules/juce_audio_basics/AppConfig.h b/source/modules/juce_audio_basics/AppConfig.h deleted file mode 100755 index 400ce44d2..000000000 --- a/source/modules/juce_audio_basics/AppConfig.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - ============================================================================== - - Build options for juce_audio_basics static library - - ============================================================================== -*/ - -#ifndef CARLA_JUCE_AUDIO_BASICS_APPCONFIG_H_INCLUDED -#define CARLA_JUCE_AUDIO_BASICS_APPCONFIG_H_INCLUDED - -#include "../juce_core/AppConfig.h" - -#endif // CARLA_JUCE_AUDIO_BASICS_APPCONFIG_H_INCLUDED diff --git a/source/modules/juce_audio_basics/Makefile b/source/modules/juce_audio_basics/Makefile index 786f8bef4..471c5245e 100644 --- a/source/modules/juce_audio_basics/Makefile +++ b/source/modules/juce_audio_basics/Makefile @@ -10,7 +10,7 @@ include ../Makefile.mk # ---------------------------------------------------------------------------------------------------------------------------- -BUILD_CXX_FLAGS += $(JUCE_AUDIO_BASICS_FLAGS) -w +BUILD_CXX_FLAGS += $(JUCE_AUDIO_BASICS_FLAGS) -I.. # ---------------------------------------------------------------------------------------------------------------------------- diff --git a/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h b/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h index e11c6c166..af9332ae3 100644 --- a/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h +++ b/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h @@ -99,7 +99,7 @@ public: class Int8 { public: - inline Int8 (void* d) noexcept : data (static_cast (d)) {} + inline Int8 (void* d) noexcept : data (static_cast (d)) {} inline void advance() noexcept { ++data; } inline void skip (int numSamples) noexcept { data += numSamples; } @@ -124,7 +124,7 @@ public: class UInt8 { public: - inline UInt8 (void* d) noexcept : data (static_cast (d)) {} + inline UInt8 (void* d) noexcept : data (static_cast (d)) {} inline void advance() noexcept { ++data; } inline void skip (int numSamples) noexcept { data += numSamples; } @@ -149,7 +149,7 @@ public: class Int16 { public: - inline Int16 (void* d) noexcept : data (static_cast (d)) {} + inline Int16 (void* d) noexcept : data (static_cast (d)) {} inline void advance() noexcept { ++data; } inline void skip (int numSamples) noexcept { data += numSamples; } @@ -174,7 +174,7 @@ public: class Int24 { public: - inline Int24 (void* d) noexcept : data (static_cast (d)) {} + inline Int24 (void* d) noexcept : data (static_cast (d)) {} inline void advance() noexcept { data += 3; } inline void skip (int numSamples) noexcept { data += 3 * numSamples; } @@ -199,7 +199,7 @@ public: class Int32 { public: - inline Int32 (void* d) noexcept : data (static_cast (d)) {} + inline Int32 (void* d) noexcept : data (static_cast (d)) {} inline void advance() noexcept { ++data; } inline void skip (int numSamples) noexcept { data += numSamples; } @@ -245,7 +245,7 @@ public: class Float32 { public: - inline Float32 (void* d) noexcept : data (static_cast (d)) {} + inline Float32 (void* d) noexcept : data (static_cast (d)) {} inline void advance() noexcept { ++data; } inline void skip (int numSamples) noexcept { data += numSamples; } @@ -318,7 +318,7 @@ public: { public: typedef const void VoidType; - static inline void* toVoidPtr (VoidType* v) noexcept { return const_cast (v); } + static inline void* toVoidPtr (VoidType* v) noexcept { return const_cast (v); } enum { isConst = 1 }; }; #endif diff --git a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp index 20fc28301..f6548ce64 100644 --- a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp +++ b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp @@ -815,11 +815,13 @@ void FloatVectorOperations::abs (float* dest, const float* src, int num) noexcep #if JUCE_USE_VDSP_FRAMEWORK vDSP_vabs ((float*) src, 1, dest, 1, (vDSP_Length) num); #else - union {float f; uint32 i;} signMask; + union { float f; uint32 i; } signMask; signMask.i = 0x7fffffffUL; JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = fabsf (src[i]), Mode::bit_and (s, mask), JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, const Mode::ParallelType mask = Mode::load1 (signMask.f);) + + ignoreUnused (signMask); #endif } @@ -834,6 +836,8 @@ void FloatVectorOperations::abs (double* dest, const double* src, int num) noexc JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = fabs (src[i]), Mode::bit_and (s, mask), JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, const Mode::ParallelType mask = Mode::load1 (signMask.d);) + + ignoreUnused (signMask); #endif } @@ -1001,7 +1005,7 @@ void JUCE_CALLTYPE FloatVectorOperations::enableFlushToZeroMode (bool shouldEnab if (FloatVectorHelpers::isSSE2Available()) _MM_SET_FLUSH_ZERO_MODE (shouldEnable ? _MM_FLUSH_ZERO_ON : _MM_FLUSH_ZERO_OFF); #endif - (void) shouldEnable; + ignoreUnused (shouldEnable); } //============================================================================== diff --git a/source/modules/juce_audio_basics/effects/juce_FFT.cpp b/source/modules/juce_audio_basics/effects/juce_FFT.cpp index dd3e7cc30..93b91ccbd 100644 --- a/source/modules/juce_audio_basics/effects/juce_FFT.cpp +++ b/source/modules/juce_audio_basics/effects/juce_FFT.cpp @@ -248,7 +248,7 @@ void FFT::performRealOnlyInverseTransform (float* d) const noexcept if (scratchSize < maxFFTScratchSpaceToAlloca) { - performRealOnlyForwardTransform (static_cast (alloca (scratchSize)), d); + performRealOnlyInverseTransform (static_cast (alloca (scratchSize)), d); } else { diff --git a/source/modules/juce_audio_basics/effects/juce_LinearSmoothedValue.h b/source/modules/juce_audio_basics/effects/juce_LinearSmoothedValue.h new file mode 100644 index 000000000..44fa1e832 --- /dev/null +++ b/source/modules/juce_audio_basics/effects/juce_LinearSmoothedValue.h @@ -0,0 +1,97 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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_LINEARSMOOTHEDVALUE_H_INCLUDED +#define JUCE_LINEARSMOOTHEDVALUE_H_INCLUDED + + +//============================================================================== +/** + Utility class for linearly smoothed values like volume etc. that should + not change abruptly but as a linear ramp, to avoid audio glitches. +*/ + +//============================================================================== +template +class JUCE_API LinearSmoothedValue +{ +public: + /** Constructor. */ + LinearSmoothedValue() noexcept + : currentValue (0), target (0), step (0), countdown (0), stepsToTarget (0) + { + } + + /** Constructor. */ + LinearSmoothedValue (FloatType initialValue) noexcept + : currentValue (initialValue), target (initialValue), step (0), countdown (0), stepsToTarget (0) + { + } + + //========================================================================== + /** Reset to a new sample rate and ramp length. */ + void reset (double sampleRate, double rampLengthInSeconds) noexcept + { + jassert (sampleRate > 0 && rampLengthInSeconds >= 0); + stepsToTarget = (int) std::floor (rampLengthInSeconds * sampleRate); + currentValue = target; + countdown = 0; + } + + //========================================================================== + /** Set a new target value. */ + void setValue (FloatType newValue) noexcept + { + if (target != newValue) + { + target = newValue; + countdown = stepsToTarget; + + if (countdown <= 0) + currentValue = target; + else + step = (target - currentValue) / (FloatType) countdown; + } + } + + //========================================================================== + /** Compute the next value. */ + FloatType getNextValue() noexcept + { + if (countdown <= 0) + return target; + + --countdown; + currentValue += step; + return currentValue; + } + +private: + //========================================================================== + FloatType currentValue, target, step; + int countdown, stepsToTarget; +}; + + +#endif // JUCE_LINEARSMOOTHEDVALUE_H_INCLUDED diff --git a/source/modules/juce_audio_basics/effects/juce_Reverb.h b/source/modules/juce_audio_basics/effects/juce_Reverb.h index 4dcc0fc34..f3dc7d342 100644 --- a/source/modules/juce_audio_basics/effects/juce_Reverb.h +++ b/source/modules/juce_audio_basics/effects/juce_Reverb.h @@ -306,54 +306,6 @@ private: JUCE_DECLARE_NON_COPYABLE (AllPassFilter) }; - //============================================================================== - class LinearSmoothedValue - { - public: - LinearSmoothedValue() noexcept - : currentValue (0), target (0), step (0), countdown (0), stepsToTarget (0) - { - } - - void reset (double sampleRate, double fadeLengthSeconds) noexcept - { - jassert (sampleRate > 0 && fadeLengthSeconds >= 0); - stepsToTarget = (int) std::floor (fadeLengthSeconds * sampleRate); - currentValue = target; - countdown = 0; - } - - void setValue (float newValue) noexcept - { - if (target != newValue) - { - target = newValue; - countdown = stepsToTarget; - - if (countdown <= 0) - currentValue = target; - else - step = (target - currentValue) / (float) 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 }; @@ -363,7 +315,7 @@ private: CombFilter comb [numChannels][numCombs]; AllPassFilter allPass [numChannels][numAllPasses]; - LinearSmoothedValue damping, feedback, dryGain, wetGain1, wetGain2; + LinearSmoothedValue damping, feedback, dryGain, wetGain1, wetGain2; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Reverb) }; diff --git a/source/modules/juce_audio_basics/juce_audio_basics.cpp b/source/modules/juce_audio_basics/juce_audio_basics.cpp index 3919502d6..5af080848 100644 --- a/source/modules/juce_audio_basics/juce_audio_basics.cpp +++ b/source/modules/juce_audio_basics/juce_audio_basics.cpp @@ -22,7 +22,7 @@ ============================================================================== */ -#if defined (JUCE_AUDIO_BASICS_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE +#ifdef JUCE_AUDIO_BASICS_H_INCLUDED /* 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 @@ -31,16 +31,13 @@ #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 -#if JUCE_MINGW +#if JUCE_MINGW && ! defined (alloca) #define alloca __builtin_alloca #endif @@ -90,6 +87,16 @@ namespace juce #include "midi/juce_MidiKeyboardState.cpp" #include "midi/juce_MidiMessage.cpp" #include "midi/juce_MidiMessageSequence.cpp" +#include "midi/juce_MidiRPN.cpp" +#include "mpe/juce_MPEValue.cpp" +#include "mpe/juce_MPENote.cpp" +#include "mpe/juce_MPEZone.cpp" +#include "mpe/juce_MPEZoneLayout.cpp" +#include "mpe/juce_MPEInstrument.cpp" +#include "mpe/juce_MPEMessages.cpp" +#include "mpe/juce_MPESynthesiserBase.cpp" +#include "mpe/juce_MPESynthesiserVoice.cpp" +#include "mpe/juce_MPESynthesiser.cpp" #include "sources/juce_BufferingAudioSource.cpp" #include "sources/juce_ChannelRemappingAudioSource.cpp" #include "sources/juce_IIRFilterAudioSource.cpp" diff --git a/source/modules/juce_audio_basics/juce_audio_basics.h b/source/modules/juce_audio_basics/juce_audio_basics.h index a58d98948..69c398c42 100644 --- a/source/modules/juce_audio_basics/juce_audio_basics.h +++ b/source/modules/juce_audio_basics/juce_audio_basics.h @@ -42,12 +42,23 @@ namespace juce #include "effects/juce_IIRFilterOld.h" #include "effects/juce_LagrangeInterpolator.h" #include "effects/juce_FFT.h" +#include "effects/juce_LinearSmoothedValue.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 "midi/juce_MidiRPN.h" +#include "mpe/juce_MPEValue.h" +#include "mpe/juce_MPENote.h" +#include "mpe/juce_MPEZone.h" +#include "mpe/juce_MPEZoneLayout.h" +#include "mpe/juce_MPEInstrument.h" +#include "mpe/juce_MPEMessages.h" +#include "mpe/juce_MPESynthesiserBase.h" +#include "mpe/juce_MPESynthesiserVoice.h" +#include "mpe/juce_MPESynthesiser.h" #include "sources/juce_AudioSource.h" #include "sources/juce_PositionableAudioSource.h" #include "sources/juce_BufferingAudioSource.h" diff --git a/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp b/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp index e24443b5a..c9abe8636 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp +++ b/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp @@ -254,12 +254,12 @@ bool MidiFile::readFrom (InputStream& sourceStream) if (sourceStream.readIntoMemoryBlock (data, maxSensibleMidiFileSize)) { size_t size = data.getSize(); - const uint8* d = static_cast (data.getData()); + 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())); + size -= (size_t) (d - static_cast (data.getData())); int track = 0; diff --git a/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp b/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp index e8adb9a80..749fe654c 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp +++ b/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp @@ -672,7 +672,7 @@ bool MidiMessage::isTextMetaEvent() const noexcept String MidiMessage::getTextFromTextMetaEvent() const { - const char* const textData = reinterpret_cast (getMetaEventData()); + const char* const textData = reinterpret_cast (getMetaEventData()); return String (CharPointer_UTF8 (textData), CharPointer_UTF8 (textData + getMetaEventLength())); } @@ -982,7 +982,7 @@ String MidiMessage::getMidiNoteName (int note, bool useSharps, bool includeOctav return s; } - return String::empty; + return String(); } double MidiMessage::getMidiNoteInHertz (int noteNumber, const double frequencyOfA) noexcept diff --git a/source/modules/juce_audio_basics/midi/juce_MidiRPN.cpp b/source/modules/juce_audio_basics/midi/juce_MidiRPN.cpp new file mode 100644 index 000000000..7d7335e2c --- /dev/null +++ b/source/modules/juce_audio_basics/midi/juce_MidiRPN.cpp @@ -0,0 +1,374 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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. + + ============================================================================== +*/ + + +MidiRPNDetector::MidiRPNDetector() noexcept +{ +} + +MidiRPNDetector::~MidiRPNDetector() noexcept +{ +} + +bool MidiRPNDetector::parseControllerMessage (int midiChannel, + int controllerNumber, + int controllerValue, + MidiRPNMessage& result) noexcept +{ + jassert (midiChannel >= 1 && midiChannel <= 16); + jassert (controllerNumber >= 0 && controllerNumber < 128); + jassert (controllerValue >= 0 && controllerValue < 128); + + return states[midiChannel - 1].handleController (midiChannel, controllerNumber, controllerValue, result); +} + +void MidiRPNDetector::reset() noexcept +{ + for (int i = 0; i < 16; ++i) + { + states[i].parameterMSB = 0xff; + states[i].parameterLSB = 0xff; + states[i].resetValue(); + states[i].isNRPN = false; + } +} + +//============================================================================== +MidiRPNDetector::ChannelState::ChannelState () noexcept + : parameterMSB (0xff), parameterLSB (0xff), valueMSB (0xff), valueLSB (0xff), isNRPN (false) +{ +} + +bool MidiRPNDetector::ChannelState::handleController (int channel, + int controllerNumber, + int value, + MidiRPNMessage& result) noexcept +{ + switch (controllerNumber) + { + case 0x62: parameterLSB = uint8 (value); resetValue(); isNRPN = true; break; + case 0x63: parameterMSB = uint8 (value); resetValue(); isNRPN = true; break; + + case 0x64: parameterLSB = uint8 (value); resetValue(); isNRPN = false; break; + case 0x65: parameterMSB = uint8 (value); resetValue(); isNRPN = false; break; + + case 0x06: valueMSB = uint8 (value); return sendIfReady (channel, result); + case 0x26: valueLSB = uint8 (value); break; + + default: break; + } + + return false; +} + +void MidiRPNDetector::ChannelState::resetValue() noexcept +{ + valueMSB = 0xff; + valueLSB = 0xff; +} + +//============================================================================== +bool MidiRPNDetector::ChannelState::sendIfReady (int channel, MidiRPNMessage& result) noexcept +{ + if (parameterMSB < 0x80 && parameterLSB < 0x80) + { + if (valueMSB < 0x80) + { + result.channel = channel; + result.parameterNumber = (parameterMSB << 7) + parameterLSB; + result.isNRPN = isNRPN; + + if (valueLSB < 0x80) + { + result.value = (valueMSB << 7) + valueLSB; + result.is14BitValue = true; + } + else + { + result.value = valueMSB; + result.is14BitValue = false; + } + + return true; + } + } + + return false; +} + +//============================================================================== +MidiBuffer MidiRPNGenerator::generate (MidiRPNMessage message) +{ + return generate (message.channel, + message.parameterNumber, + message.value, + message.isNRPN, + message.is14BitValue); +} + +MidiBuffer MidiRPNGenerator::generate (int midiChannel, + int parameterNumber, + int value, + bool isNRPN, + bool use14BitValue) +{ + jassert (midiChannel > 0 && midiChannel <= 16); + jassert (parameterNumber >= 0 && parameterNumber < 16384); + jassert (value >= 0 && value < (use14BitValue ? 16384 : 128)); + + uint8 parameterLSB = uint8 (parameterNumber & 0x0000007f); + uint8 parameterMSB = uint8 (parameterNumber >> 7); + + uint8 valueLSB = use14BitValue ? uint8 (value & 0x0000007f) : 0x00; + uint8 valueMSB = use14BitValue ? uint8 (value >> 7) : uint8 (value); + + uint8 channelByte = uint8 (0xb0 + midiChannel - 1); + + MidiBuffer buffer; + + buffer.addEvent (MidiMessage (channelByte, isNRPN ? 0x62 : 0x64, parameterLSB), 0); + buffer.addEvent (MidiMessage (channelByte, isNRPN ? 0x63 : 0x65, parameterMSB), 0); + + // sending the value LSB is optional, but must come before sending the value MSB: + if (use14BitValue) + buffer.addEvent (MidiMessage (channelByte, 0x26, valueLSB), 0); + + buffer.addEvent (MidiMessage (channelByte, 0x06, valueMSB), 0); + + return buffer; +} + +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +class MidiRPNDetectorTests : public UnitTest +{ +public: + MidiRPNDetectorTests() : UnitTest ("MidiRPNDetector class") {} + + void runTest() override + { + beginTest ("7-bit RPN"); + { + MidiRPNDetector detector; + MidiRPNMessage rpn; + expect (! detector.parseControllerMessage (2, 101, 0, rpn)); + expect (! detector.parseControllerMessage (2, 100, 7, rpn)); + expect (detector.parseControllerMessage (2, 6, 42, rpn)); + + expectEquals (rpn.channel, 2); + expectEquals (rpn.parameterNumber, 7); + expectEquals (rpn.value, 42); + expect (! rpn.isNRPN); + expect (! rpn.is14BitValue); + } + + beginTest ("14-bit RPN"); + { + MidiRPNDetector detector; + MidiRPNMessage rpn; + expect (! detector.parseControllerMessage (1, 100, 44, rpn)); + expect (! detector.parseControllerMessage (1, 101, 2, rpn)); + expect (! detector.parseControllerMessage (1, 38, 94, rpn)); + expect (detector.parseControllerMessage (1, 6, 1, rpn)); + + expectEquals (rpn.channel, 1); + expectEquals (rpn.parameterNumber, 300); + expectEquals (rpn.value, 222); + expect (! rpn.isNRPN); + expect (rpn.is14BitValue); + } + + beginTest ("RPNs on multiple channels simultaneously"); + { + MidiRPNDetector detector; + MidiRPNMessage rpn; + expect (! detector.parseControllerMessage (1, 100, 44, rpn)); + expect (! detector.parseControllerMessage (2, 101, 0, rpn)); + expect (! detector.parseControllerMessage (1, 101, 2, rpn)); + expect (! detector.parseControllerMessage (2, 100, 7, rpn)); + expect (! detector.parseControllerMessage (1, 38, 94, rpn)); + expect (detector.parseControllerMessage (2, 6, 42, rpn)); + + expectEquals (rpn.channel, 2); + expectEquals (rpn.parameterNumber, 7); + expectEquals (rpn.value, 42); + expect (! rpn.isNRPN); + expect (! rpn.is14BitValue); + + expect (detector.parseControllerMessage (1, 6, 1, rpn)); + + expectEquals (rpn.channel, 1); + expectEquals (rpn.parameterNumber, 300); + expectEquals (rpn.value, 222); + expect (! rpn.isNRPN); + expect (rpn.is14BitValue); + } + + beginTest ("14-bit RPN with value within 7-bit range"); + { + MidiRPNDetector detector; + MidiRPNMessage rpn; + expect (! detector.parseControllerMessage (16, 100, 0 , rpn)); + expect (! detector.parseControllerMessage (16, 101, 0, rpn)); + expect (! detector.parseControllerMessage (16, 38, 3, rpn)); + expect (detector.parseControllerMessage (16, 6, 0, rpn)); + + expectEquals (rpn.channel, 16); + expectEquals (rpn.parameterNumber, 0); + expectEquals (rpn.value, 3); + expect (! rpn.isNRPN); + expect (rpn.is14BitValue); + } + + beginTest ("invalid RPN (wrong order)"); + { + MidiRPNDetector detector; + MidiRPNMessage rpn; + expect (! detector.parseControllerMessage (2, 6, 42, rpn)); + expect (! detector.parseControllerMessage (2, 101, 0, rpn)); + expect (! detector.parseControllerMessage (2, 100, 7, rpn)); + } + + beginTest ("14-bit RPN interspersed with unrelated CC messages"); + { + MidiRPNDetector detector; + MidiRPNMessage rpn; + expect (! detector.parseControllerMessage (16, 3, 80, rpn)); + expect (! detector.parseControllerMessage (16, 100, 0 , rpn)); + expect (! detector.parseControllerMessage (16, 4, 81, rpn)); + expect (! detector.parseControllerMessage (16, 101, 0, rpn)); + expect (! detector.parseControllerMessage (16, 5, 82, rpn)); + expect (! detector.parseControllerMessage (16, 5, 83, rpn)); + expect (! detector.parseControllerMessage (16, 38, 3, rpn)); + expect (! detector.parseControllerMessage (16, 4, 84, rpn)); + expect (! detector.parseControllerMessage (16, 3, 85, rpn)); + expect (detector.parseControllerMessage (16, 6, 0, rpn)); + + expectEquals (rpn.channel, 16); + expectEquals (rpn.parameterNumber, 0); + expectEquals (rpn.value, 3); + expect (! rpn.isNRPN); + expect (rpn.is14BitValue); + } + + beginTest ("14-bit NRPN"); + { + MidiRPNDetector detector; + MidiRPNMessage rpn; + expect (! detector.parseControllerMessage (1, 98, 44, rpn)); + expect (! detector.parseControllerMessage (1, 99 , 2, rpn)); + expect (! detector.parseControllerMessage (1, 38, 94, rpn)); + expect (detector.parseControllerMessage (1, 6, 1, rpn)); + + expectEquals (rpn.channel, 1); + expectEquals (rpn.parameterNumber, 300); + expectEquals (rpn.value, 222); + expect (rpn.isNRPN); + expect (rpn.is14BitValue); + } + + beginTest ("reset"); + { + MidiRPNDetector detector; + MidiRPNMessage rpn; + expect (! detector.parseControllerMessage (2, 101, 0, rpn)); + detector.reset(); + expect (! detector.parseControllerMessage (2, 100, 7, rpn)); + expect (! detector.parseControllerMessage (2, 6, 42, rpn)); + } + } +}; + +static MidiRPNDetectorTests MidiRPNDetectorUnitTests; + +//============================================================================== +class MidiRPNGeneratorTests : public UnitTest +{ +public: + MidiRPNGeneratorTests() : UnitTest ("MidiRPNGenerator class") {} + + void runTest() override + { + beginTest ("generating RPN/NRPN"); + { + { + MidiBuffer buffer = MidiRPNGenerator::generate (1, 23, 1337, true, true); + expectContainsRPN (buffer, 1, 23, 1337, true, true); + } + { + MidiBuffer buffer = MidiRPNGenerator::generate (16, 101, 34, false, false); + expectContainsRPN (buffer, 16, 101, 34, false, false); + } + { + MidiRPNMessage message = { 16, 101, 34, false, false }; + MidiBuffer buffer = MidiRPNGenerator::generate (message); + expectContainsRPN (buffer, message); + } + } + } + +private: + //========================================================================== + void expectContainsRPN (const MidiBuffer& midiBuffer, + int channel, + int parameterNumber, + int value, + bool isNRPN, + bool is14BitValue) + { + MidiRPNMessage expected = { channel, parameterNumber, value, isNRPN, is14BitValue }; + expectContainsRPN (midiBuffer, expected); + } + + //========================================================================== + void expectContainsRPN (const MidiBuffer& midiBuffer, MidiRPNMessage expected) + { + MidiBuffer::Iterator iter (midiBuffer); + MidiMessage midiMessage; + MidiRPNMessage result = MidiRPNMessage(); + MidiRPNDetector detector; + int samplePosition; // not actually used, so no need to initialise. + + while (iter.getNextEvent (midiMessage, samplePosition)) + { + if (detector.parseControllerMessage (midiMessage.getChannel(), + midiMessage.getControllerNumber(), + midiMessage.getControllerValue(), + result)) + break; + } + + expectEquals (result.channel, expected.channel); + expectEquals (result.parameterNumber, expected.parameterNumber); + expectEquals (result.value, expected.value); + expect (result.isNRPN == expected.isNRPN), + expect (result.is14BitValue == expected.is14BitValue); + } +}; + +static MidiRPNGeneratorTests MidiRPNGeneratorUnitTests; + +#endif // JUCE_UNIT_TESTS diff --git a/source/modules/juce_audio_basics/midi/juce_MidiRPN.h b/source/modules/juce_audio_basics/midi/juce_MidiRPN.h new file mode 100644 index 000000000..9199860f1 --- /dev/null +++ b/source/modules/juce_audio_basics/midi/juce_MidiRPN.h @@ -0,0 +1,152 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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_MIDIRPNDETECTOR_H_INCLUDED +#define JUCE_MIDIRPNDETECTOR_H_INCLUDED + + +//========================================================================== +/** Represents a MIDI RPN (registered parameter number) or NRPN (non-registered + parameter number) message. +*/ +struct MidiRPNMessage +{ + /** Midi channel of the message, in the range 1 to 16. */ + int channel; + + /** The 14-bit parameter index, in the range 0 to 16383 (0x3fff). */ + int parameterNumber; + + /** The parameter value, in the range 0 to 16383 (0x3fff). + If the message contains no value LSB, the value will be in the range + 0 to 127 (0x7f). + */ + int value; + + /** True if this message is an NRPN; false if it is an RPN. */ + bool isNRPN; + + /** True if the value uses 14-bit resolution (LSB + MSB); false if + the value is 7-bit (MSB only). + */ + bool is14BitValue; +}; + +//============================================================================== +/** + Parses a stream of MIDI data to assemble RPN and NRPN messages from their + constituent MIDI CC messages. + + The detector uses the following parsing rules: the parameter number + LSB/MSB can be sent/received in either order and must both come before the + parameter value; for the parameter value, LSB always has to be sent/received + before the value MSB, otherwise it will be treated as 7-bit (MSB only). +*/ +class JUCE_API MidiRPNDetector +{ +public: + /** Constructor. */ + MidiRPNDetector() noexcept; + + /** Destructor. */ + ~MidiRPNDetector() noexcept; + + /** Resets the RPN detector's internal state, so that it forgets about + previously received MIDI CC messages. + */ + void reset() noexcept; + + //========================================================================== + /** Takes the next in a stream of incoming MIDI CC messages and returns true + if it forms the last of a sequence that makes an RPN or NPRN. + + If this returns true, then the RPNMessage object supplied will be + filled-out with the message's details. + (If it returns false then the RPNMessage object will be unchanged). + */ + bool parseControllerMessage (int midiChannel, + int controllerNumber, + int controllerValue, + MidiRPNMessage& result) noexcept; + +private: + //========================================================================== + struct ChannelState + { + ChannelState() noexcept; + bool handleController (int channel, int controllerNumber, + int value, MidiRPNMessage&) noexcept; + void resetValue() noexcept; + bool sendIfReady (int channel, MidiRPNMessage&) noexcept; + + uint8 parameterMSB, parameterLSB, valueMSB, valueLSB; + bool isNRPN; + }; + + //========================================================================== + ChannelState states[16]; + + JUCE_LEAK_DETECTOR (MidiRPNDetector) +}; + +//============================================================================== +/** + Generates an appropriate sequence of MIDI CC messages to represent an RPN + or NRPN message. + + This sequence (as a MidiBuffer) can then be directly sent to a MidiOutput. +*/ +class JUCE_API MidiRPNGenerator +{ +public: + //========================================================================== + /** Generates a MIDI sequence representing the given RPN or NRPN message. */ + static MidiBuffer generate (MidiRPNMessage message); + + //========================================================================== + /** Generates a MIDI sequence representing an RPN or NRPN message with the + given parameters. + + @param channel The MIDI channel of the RPN/NRPN message. + + @param parameterNumber The parameter number, in the range 0 to 16383. + + @param value The parameter value, in the range 0 to 16383, or + in the range 0 to 127 if sendAs14BitValue is false. + + @param isNRPN Whether you need a MIDI RPN or NRPN sequence (RPN is default). + + @param use14BitValue If true (default), the value will have 14-bit precision + (two MIDI bytes). If false, instead the value will have + 7-bit presision (a single MIDI byte). + */ + static MidiBuffer generate (int channel, + int parameterNumber, + int value, + bool isNRPN = false, + bool use14BitValue = true); +}; + + +#endif // JUCE_MIDIRPNDETECTOR_H_INCLUDED diff --git a/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp b/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp new file mode 100644 index 000000000..cabee295d --- /dev/null +++ b/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp @@ -0,0 +1,2150 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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 +{ + const uint8 noLSBValueReceived = 0xff; + const Range allChannels = Range (1, 17); +} + +//============================================================================== +MPEInstrument::MPEInstrument() noexcept +{ + std::fill_n (lastPressureLowerBitReceivedOnChannel, 16, noLSBValueReceived); + std::fill_n (lastTimbreLowerBitReceivedOnChannel, 16, noLSBValueReceived); + std::fill_n (isNoteChannelSustained, 16, false); + + pitchbendDimension.value = &MPENote::pitchbend; + pressureDimension.value = &MPENote::pressure; + timbreDimension.value = &MPENote::timbre; + + legacyMode.isEnabled = false; + legacyMode.pitchbendRange = 2; + legacyMode.channelRange = Range (1, 17); +} + +MPEInstrument::~MPEInstrument() +{ +} + +//============================================================================== +MPEZoneLayout MPEInstrument::getZoneLayout() const noexcept +{ + return zoneLayout; +} + +void MPEInstrument::setZoneLayout (MPEZoneLayout newLayout) +{ + releaseAllNotes(); + + const ScopedLock sl (lock); + legacyMode.isEnabled = false; + zoneLayout = newLayout; +} + +//============================================================================== +void MPEInstrument::enableLegacyMode (int pitchbendRange, Range channelRange) +{ + releaseAllNotes(); + + const ScopedLock sl (lock); + legacyMode.isEnabled = true; + legacyMode.pitchbendRange = pitchbendRange; + legacyMode.channelRange = channelRange; + zoneLayout.clearAllZones(); +} + +bool MPEInstrument::isLegacyModeEnabled() const noexcept +{ + return legacyMode.isEnabled; +} + +Range MPEInstrument::getLegacyModeChannelRange() const noexcept +{ + return legacyMode.channelRange; +} + +void MPEInstrument::setLegacyModeChannelRange (Range channelRange) +{ + jassert (Range(1, 17).contains (channelRange)); + + releaseAllNotes(); + const ScopedLock sl (lock); + legacyMode.channelRange = channelRange; +} + +int MPEInstrument::getLegacyModePitchbendRange() const noexcept +{ + return legacyMode.pitchbendRange; +} + +void MPEInstrument::setLegacyModePitchbendRange (int pitchbendRange) +{ + jassert (pitchbendRange >= 0 && pitchbendRange <= 96); + + releaseAllNotes(); + const ScopedLock sl (lock); + legacyMode.pitchbendRange = pitchbendRange; +} + +//============================================================================== +void MPEInstrument::setPressureTrackingMode (TrackingMode modeToUse) +{ + pressureDimension.trackingMode = modeToUse; +} + +void MPEInstrument::setPitchbendTrackingMode (TrackingMode modeToUse) +{ + pitchbendDimension.trackingMode = modeToUse; +} + +void MPEInstrument::setTimbreTrackingMode (TrackingMode modeToUse) +{ + timbreDimension.trackingMode = modeToUse; +} + +//============================================================================== +void MPEInstrument::addListener (Listener* const listenerToAdd) noexcept +{ + listeners.add (listenerToAdd); +} + +void MPEInstrument::removeListener (Listener* const listenerToRemove) noexcept +{ + listeners.remove (listenerToRemove); +} + +MPEInstrument::Listener::Listener() +{ +} + +MPEInstrument::Listener::~Listener() +{ +} + +//============================================================================== +void MPEInstrument::processNextMidiEvent (const MidiMessage& message) +{ + zoneLayout.processNextMidiEvent (message); + + if (message.isNoteOn (true)) processMidiNoteOnMessage (message); + else if (message.isNoteOff (false)) processMidiNoteOffMessage (message); + else if (message.isAllNotesOff()) processMidiAllNotesOffMessage (message); + else if (message.isPitchWheel()) processMidiPitchWheelMessage (message); + else if (message.isChannelPressure()) processMidiChannelPressureMessage (message); + else if (message.isController()) processMidiControllerMessage (message); +} + +//============================================================================== +void MPEInstrument::processMidiNoteOnMessage (const MidiMessage& message) +{ + // Note: if a note-on with velocity = 0 is used to convey a note-off, + // then the actual note-off velocity is not known. In this case, + // the MPE convention is to use note-off velocity = 64. + + if (message.getVelocity() == 0) + { + noteOff (message.getChannel(), + message.getNoteNumber(), + MPEValue::from7BitInt (64)); + } + else + { + noteOn (message.getChannel(), + message.getNoteNumber(), + MPEValue::from7BitInt (message.getVelocity())); + } +} + +//============================================================================== +void MPEInstrument::processMidiNoteOffMessage (const MidiMessage& message) +{ + noteOff (message.getChannel(), + message.getNoteNumber(), + MPEValue::from7BitInt (message.getVelocity())); +} + +//============================================================================== +void MPEInstrument::processMidiPitchWheelMessage (const MidiMessage& message) +{ + pitchbend (message.getChannel(), + MPEValue::from14BitInt (message.getPitchWheelValue())); +} + +//============================================================================== +void MPEInstrument::processMidiChannelPressureMessage (const MidiMessage& message) +{ + pressure (message.getChannel(), + MPEValue::from7BitInt (message.getChannelPressureValue())); +} + +//============================================================================== +void MPEInstrument::processMidiControllerMessage (const MidiMessage& message) +{ + switch (message.getControllerNumber()) + { + case 64: sustainPedal (message.getChannel(), message.isSustainPedalOn()); break; + case 66: sostenutoPedal (message.getChannel(), message.isSostenutoPedalOn()); break; + case 70: handlePressureMSB (message.getChannel(), message.getControllerValue()); break; + case 74: handleTimbreMSB (message.getChannel(), message.getControllerValue()); break; + case 102: handlePressureLSB (message.getChannel(), message.getControllerValue()); break; + case 106: handleTimbreLSB (message.getChannel(), message.getControllerValue()); break; + default: break; + } +} + +//============================================================================== +void MPEInstrument::processMidiAllNotesOffMessage (const MidiMessage& message) +{ + // in MPE mode, "all notes off" is per-zone and expected on the master channel; + // in legacy mode, "all notes off" is per MIDI channel (within the channel range used). + + if (legacyMode.isEnabled && legacyMode.channelRange.contains (message.getChannel())) + { + for (int i = notes.size(); --i >= 0;) + { + MPENote& note = notes.getReference (i); + + if (note.midiChannel == message.getChannel()) + { + note.keyState = MPENote::off; + note.noteOffVelocity = MPEValue::from7BitInt (64); // some reasonable number + listeners.call (&MPEInstrument::Listener::noteReleased, note); + notes.remove (i); + } + } + } + else if (MPEZone* zone = zoneLayout.getZoneByMasterChannel (message.getChannel())) + { + for (int i = notes.size(); --i >= 0;) + { + MPENote& note = notes.getReference (i); + + if (zone->isUsingChannelAsNoteChannel (note.midiChannel)) + { + note.keyState = MPENote::off; + note.noteOffVelocity = MPEValue::from7BitInt (64); // some reasonable number + listeners.call (&MPEInstrument::Listener::noteReleased, note); + notes.remove (i); + } + } + } +} + +//============================================================================== +void MPEInstrument::handlePressureMSB (int midiChannel, int value) noexcept +{ + const uint8 lsb = lastPressureLowerBitReceivedOnChannel[midiChannel - 1]; + + pressure (midiChannel, lsb == noLSBValueReceived ? MPEValue::from7BitInt (value) + : MPEValue::from14BitInt (lsb + (value << 7))); +} + +void MPEInstrument::handlePressureLSB (int midiChannel, int value) noexcept +{ + lastPressureLowerBitReceivedOnChannel[midiChannel - 1] = uint8 (value); +} + +void MPEInstrument::handleTimbreMSB (int midiChannel, int value) noexcept +{ + const uint8 lsb = lastTimbreLowerBitReceivedOnChannel[midiChannel - 1]; + + timbre (midiChannel, lsb == noLSBValueReceived ? MPEValue::from7BitInt (value) + : MPEValue::from14BitInt (lsb + (value << 7))); +} + +void MPEInstrument::handleTimbreLSB (int midiChannel, int value) noexcept +{ + lastTimbreLowerBitReceivedOnChannel[midiChannel - 1] = uint8 (value); +} + +//============================================================================== +MPEValue MPEInstrument::getInitialPitchbendForNoteOn (int midiChannel, int /*midiNoteNumber*/, MPEValue /*midiNoteOnVelocity*/) const +{ + return pitchbendDimension.lastValueReceivedOnChannel[midiChannel - 1]; +} + +MPEValue MPEInstrument::getInitialPressureForNoteOn (int /*midiChannel*/, int /*midiNoteNumber*/, MPEValue midiNoteOnVelocity) const +{ + return midiNoteOnVelocity; +} + +MPEValue MPEInstrument::getInitialTimbreForNoteOn (int midiChannel, int /*midiNoteNumber*/, MPEValue /*midiNoteOnVelocity*/) const +{ + return timbreDimension.lastValueReceivedOnChannel[midiChannel - 1]; +} + +//============================================================================== +void MPEInstrument::noteOn (int midiChannel, + int midiNoteNumber, + MPEValue midiNoteOnVelocity) +{ + if (! isNoteChannel (midiChannel)) + return; + + MPENote newNote (midiChannel, + midiNoteNumber, + midiNoteOnVelocity, + getInitialPitchbendForNoteOn (midiChannel, midiNoteNumber, midiNoteOnVelocity), + getInitialPressureForNoteOn (midiChannel, midiNoteNumber, midiNoteOnVelocity), + getInitialTimbreForNoteOn (midiChannel, midiNoteNumber, midiNoteOnVelocity), + isNoteChannelSustained[midiChannel - 1] ? MPENote::keyDownAndSustained : MPENote::keyDown); + + const ScopedLock sl (lock); + updateNoteTotalPitchbend (newNote); + + if (MPENote* alreadyPlayingNote = getNotePtr (midiChannel, midiNoteNumber)) + { + // pathological case: second note-on received for same note -> retrigger it + alreadyPlayingNote->keyState = MPENote::off; + alreadyPlayingNote->noteOffVelocity = MPEValue::from7BitInt (64); // some reasonable number + listeners.call (&MPEInstrument::Listener::noteReleased, *alreadyPlayingNote); + notes.remove (alreadyPlayingNote); + } + + notes.add (newNote); + listeners.call (&MPEInstrument::Listener::noteAdded, newNote); +} + +//============================================================================== +void MPEInstrument::noteOff (int midiChannel, + int midiNoteNumber, + MPEValue midiNoteOffVelocity) +{ + if (notes.empty() || ! isNoteChannel (midiChannel)) + return; + + const ScopedLock sl (lock); + + if (MPENote* note = getNotePtr (midiChannel, midiNoteNumber)) + { + note->keyState = (note->keyState == MPENote::keyDownAndSustained) ? MPENote::sustained : MPENote::off; + note->noteOffVelocity = midiNoteOffVelocity; + + // last pitchbend and timbre values received for this note should not be re-used for + // any new notes, so reset them: + pitchbendDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue(); + timbreDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue(); + + if (note->keyState == MPENote::off) + { + listeners.call (&MPEInstrument::Listener::noteReleased, *note); + notes.remove (note); + } + else + { + listeners.call (&MPEInstrument::Listener::noteKeyStateChanged, *note); + } + } +} + +//============================================================================== +void MPEInstrument::pitchbend (int midiChannel, MPEValue value) +{ + const ScopedLock sl (lock); + updateDimension (midiChannel, pitchbendDimension, value); +} + +void MPEInstrument::pressure (int midiChannel, MPEValue value) +{ + const ScopedLock sl (lock); + updateDimension (midiChannel, pressureDimension, value); +} + +void MPEInstrument::timbre (int midiChannel, MPEValue value) +{ + const ScopedLock sl (lock); + updateDimension (midiChannel, timbreDimension, value); +} + +//============================================================================== +void MPEInstrument::updateDimension (int midiChannel, MPEDimension& dimension, MPEValue value) +{ + dimension.lastValueReceivedOnChannel[midiChannel - 1] = value; + + if (notes.empty()) + return; + + if (MPEZone* zone = zoneLayout.getZoneByMasterChannel (midiChannel)) + { + updateDimensionMaster (*zone, dimension, value); + } + else if (isNoteChannel (midiChannel)) + { + if (dimension.trackingMode == allNotesOnChannel) + { + for (int i = notes.size(); --i >= 0;) + { + MPENote& note = notes.getReference (i); + + if (note.midiChannel == midiChannel) + updateDimensionForNote (note, dimension, value); + } + } + else + { + if (MPENote* note = getNotePtr (midiChannel, dimension.trackingMode)) + updateDimensionForNote (*note, dimension, value); + } + } +} + +//============================================================================== +void MPEInstrument::updateDimensionMaster (MPEZone& zone, MPEDimension& dimension, MPEValue value) +{ + const Range channels (zone.getNoteChannelRange()); + + for (int i = notes.size(); --i >= 0;) + { + MPENote& note = notes.getReference (i); + + if (! channels.contains (note.midiChannel)) + continue; + + if (&dimension == &pitchbendDimension) + { + // master pitchbend is a special case: we don't change the note's own pitchbend, + // instead we have to update its total (master + note) pitchbend. + updateNoteTotalPitchbend (note); + listeners.call (&MPEInstrument::Listener::notePitchbendChanged, note); + } + else if (dimension.getValue (note) != value) + { + dimension.getValue (note) = value; + callListenersDimensionChanged (note, dimension); + } + } +} + +//============================================================================== +void MPEInstrument::updateDimensionForNote (MPENote& note, MPEDimension& dimension, MPEValue value) +{ + if (dimension.getValue (note) != value) + { + dimension.getValue (note) = value; + + if (&dimension == &pitchbendDimension) + updateNoteTotalPitchbend (note); + + callListenersDimensionChanged (note, dimension); + } +} + +//============================================================================== +void MPEInstrument::callListenersDimensionChanged (MPENote& note, MPEDimension& dimension) +{ + if (&dimension == &pressureDimension) { listeners.call (&MPEInstrument::Listener::notePressureChanged, note); return; } + if (&dimension == &timbreDimension) { listeners.call (&MPEInstrument::Listener::noteTimbreChanged, note); return; } + if (&dimension == &pitchbendDimension) { listeners.call (&MPEInstrument::Listener::notePitchbendChanged, note); return; } +} + +//============================================================================== +void MPEInstrument::updateNoteTotalPitchbend (MPENote& note) +{ + if (legacyMode.isEnabled) + { + note.totalPitchbendInSemitones = note.pitchbend.asSignedFloat() * legacyMode.pitchbendRange; + } + else + { + if (MPEZone* zone = zoneLayout.getZoneByNoteChannel (note.midiChannel)) + { + double notePitchbendInSemitones = note.pitchbend.asSignedFloat() * zone->getPerNotePitchbendRange(); + double masterPitchbendInSemitones = pitchbendDimension.lastValueReceivedOnChannel[zone->getMasterChannel() - 1].asSignedFloat() * zone->getMasterPitchbendRange(); + note.totalPitchbendInSemitones = notePitchbendInSemitones + masterPitchbendInSemitones; + } + else + { + // oops - this note seems to not belong to any zone! + jassertfalse; + } + } +} + +//============================================================================== +void MPEInstrument::sustainPedal (int midiChannel, bool isDown) +{ + const ScopedLock sl (lock); + handleSustainOrSostenuto (midiChannel, isDown, false); +} + +void MPEInstrument::sostenutoPedal (int midiChannel, bool isDown) +{ + const ScopedLock sl (lock); + handleSustainOrSostenuto (midiChannel, isDown, true); +} + +//============================================================================== +void MPEInstrument::handleSustainOrSostenuto (int midiChannel, bool isDown, bool isSostenuto) +{ + // in MPE mode, sustain/sostenuto is per-zone and expected on the master channel; + // in legacy mode, sustain/sostenuto is per MIDI channel (within the channel range used). + + MPEZone* affectedZone = zoneLayout.getZoneByMasterChannel (midiChannel); + + if (legacyMode.isEnabled ? (! legacyMode.channelRange.contains (midiChannel)) : (affectedZone == nullptr)) + return; + + for (int i = notes.size(); --i >= 0;) + { + MPENote& note = notes.getReference (i); + + if (legacyMode.isEnabled ? (note.midiChannel == midiChannel) : affectedZone->isUsingChannel (note.midiChannel)) + { + if (note.keyState == MPENote::keyDown && isDown) + note.keyState = MPENote::keyDownAndSustained; + else if (note.keyState == MPENote::sustained && ! isDown) + note.keyState = MPENote::off; + else if (note.keyState == MPENote::keyDownAndSustained && ! isDown) + note.keyState = MPENote::keyDown; + + if (note.keyState == MPENote::off) + { + listeners.call (&MPEInstrument::Listener::noteReleased, note); + notes.remove (i); + } + else + { + listeners.call (&MPEInstrument::Listener::noteKeyStateChanged, note); + } + } + } + + if (! isSostenuto) + { + if (legacyMode.isEnabled) + isNoteChannelSustained[midiChannel - 1] = isDown; + else + for (int i = affectedZone->getFirstNoteChannel(); i <= affectedZone->getLastNoteChannel(); ++i) + isNoteChannelSustained[i - 1] = isDown; + } +} + +//============================================================================== +bool MPEInstrument::isNoteChannel (int midiChannel) const noexcept +{ + if (legacyMode.isEnabled) + return legacyMode.channelRange.contains (midiChannel); + + return zoneLayout.getZoneByNoteChannel (midiChannel) != nullptr; +} + +bool MPEInstrument::isMasterChannel (int midiChannel) const noexcept +{ + if (legacyMode.isEnabled) + return false; + + return zoneLayout.getZoneByMasterChannel (midiChannel) != nullptr; +} + +//============================================================================== +int MPEInstrument::getNumPlayingNotes() const noexcept +{ + return notes.size(); +} + +MPENote MPEInstrument::getNote (int midiChannel, int midiNoteNumber) const noexcept +{ + if (MPENote* note = getNotePtr (midiChannel, midiNoteNumber)) + return *note; + + return MPENote(); +} + +MPENote MPEInstrument::getNote (int index) const noexcept +{ + return notes[index]; +} + +//============================================================================== +MPENote MPEInstrument::getMostRecentNote (int midiChannel) const noexcept +{ + if (MPENote* note = getLastNotePlayedPtr (midiChannel)) + return *note; + + return MPENote(); +} + +MPENote MPEInstrument::getMostRecentNoteOtherThan (MPENote otherThanThisNote) const noexcept +{ + for (int i = notes.size(); --i >= 0;) + { + const MPENote& note = notes.getReference (i); + + if (note != otherThanThisNote) + return note; + } + + return MPENote(); +} + +//============================================================================== +MPENote* MPEInstrument::getNotePtr (int midiChannel, int midiNoteNumber) const noexcept +{ + for (int i = 0; i < notes.size(); ++i) + { + MPENote& note = notes.getReference (i); + + if (note.midiChannel == midiChannel && note.initialNote == midiNoteNumber) + return ¬e; + } + + return nullptr; +} + +//============================================================================== +MPENote* MPEInstrument::getNotePtr (int midiChannel, TrackingMode mode) const noexcept +{ + // for the "all notes" tracking mode, this method can never possibly + // work because it returns 0 or 1 note but there might be more than one! + jassert (mode != allNotesOnChannel); + + if (mode == lastNotePlayedOnChannel) return getLastNotePlayedPtr (midiChannel); + if (mode == lowestNoteOnChannel) return getLowestNotePtr (midiChannel); + if (mode == highestNoteOnChannel) return getHighestNotePtr (midiChannel); + + return nullptr; +} + +//============================================================================== +MPENote* MPEInstrument::getLastNotePlayedPtr (int midiChannel) const noexcept +{ + for (int i = notes.size(); --i >= 0;) + { + MPENote& note = notes.getReference (i); + + if (note.midiChannel == midiChannel + && (note.keyState == MPENote::keyDown || note.keyState == MPENote::keyDownAndSustained)) + return ¬e; + } + + return nullptr; +} + +//============================================================================== +MPENote* MPEInstrument::getHighestNotePtr (int midiChannel) const noexcept +{ + int initialNoteMax = -1; + MPENote* result = nullptr; + + for (int i = notes.size(); --i >= 0;) + { + MPENote& note = notes.getReference (i); + + if (note.midiChannel == midiChannel + && (note.keyState == MPENote::keyDown || note.keyState == MPENote::keyDownAndSustained) + && note.initialNote > initialNoteMax) + { + result = ¬e; + initialNoteMax = note.initialNote; + } + } + + return result; +} + +MPENote* MPEInstrument::getLowestNotePtr (int midiChannel) const noexcept +{ + int initialNoteMin = 128; + MPENote* result = nullptr; + + for (int i = notes.size(); --i >= 0;) + { + MPENote& note = notes.getReference (i); + + if (note.midiChannel == midiChannel + && (note.keyState == MPENote::keyDown || note.keyState == MPENote::keyDownAndSustained) + && note.initialNote < initialNoteMin) + { + result = ¬e; + initialNoteMin = note.initialNote; + } + } + + return result; +} + +//============================================================================== +void MPEInstrument::releaseAllNotes() +{ + const ScopedLock sl (lock); + + for (int i = notes.size(); --i >= 0;) + { + MPENote& note = notes.getReference (i); + note.keyState = MPENote::off; + note.noteOffVelocity = MPEValue::from7BitInt (64); // some reasonable number + listeners.call (&MPEInstrument::Listener::noteReleased, note); + } + + notes.clear(); +} + +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +class MPEInstrumentTests : public UnitTest +{ +public: + MPEInstrumentTests() + : UnitTest ("MPEInstrument class") + { + // using two MPE zones with the following layout for testing + // + // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + // * ...................| * ........................| + + testLayout.addZone (MPEZone (2, 5)); + testLayout.addZone (MPEZone (9, 6)); + } + + void runTest() override + { + beginTest ("initial zone layout"); + { + MPEInstrument test; + expectEquals (test.getZoneLayout().getNumZones(), 0); + } + + beginTest ("get/setZoneLayout"); + { + MPEInstrument test; + test.setZoneLayout (testLayout); + + MPEZoneLayout newLayout = test.getZoneLayout(); + expectEquals (newLayout.getNumZones(), 2); + expectEquals (newLayout.getZoneByIndex (0)->getMasterChannel(), 2); + expectEquals (newLayout.getZoneByIndex (0)->getNumNoteChannels(), 5); + expectEquals (newLayout.getZoneByIndex (1)->getMasterChannel(), 9); + expectEquals (newLayout.getZoneByIndex (1)->getNumNoteChannels(), 6); + } + + beginTest ("noteOn / noteOff"); + { + { + MPEInstrument test; + test.setZoneLayout (testLayout); + expectEquals (test.getNumPlayingNotes(), 0); + } + { + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + // note-on on master channel - ignore + test.noteOn (9, 60, MPEValue::from7BitInt (100)); + expectEquals (test.getNumPlayingNotes(), 0); + expectEquals (test.noteAddedCallCounter, 0); + + // note-on on any other channel - ignore + test.noteOn (1, 60, MPEValue::from7BitInt (100)); + expectEquals (test.getNumPlayingNotes(), 0); + expectEquals (test.noteAddedCallCounter, 0); + + // note-on on note channel - create new note + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + expectEquals (test.getNumPlayingNotes(), 1); + expectEquals (test.noteAddedCallCounter, 1); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + + // note-off + test.noteOff (3, 60, MPEValue::from7BitInt (33)); + expectEquals (test.getNumPlayingNotes(), 0); + expectEquals (test.noteReleasedCallCounter, 1); + expectHasFinishedNote (test, 3, 60, 33); + } + { + UnitTestInstrument test; + test.setZoneLayout (testLayout); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + + // note off with non-matching note number shouldn't do anything + test.noteOff (3, 61, MPEValue::from7BitInt (33)); + expectEquals (test.getNumPlayingNotes(), 1); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.noteReleasedCallCounter, 0); + + // note off with non-matching midi channel shouldn't do anything + test.noteOff (2, 60, MPEValue::from7BitInt (33)); + expectEquals (test.getNumPlayingNotes(), 1); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.noteReleasedCallCounter, 0); + } + { + // can have multiple notes on the same channel + UnitTestInstrument test; + test.setZoneLayout (testLayout); + test.noteOn (3, 0, MPEValue::from7BitInt (100)); + test.noteOn (3, 1, MPEValue::from7BitInt (100)); + test.noteOn (3, 2, MPEValue::from7BitInt (100)); + expectEquals (test.getNumPlayingNotes(), 3); + expectNote (test.getNote (3, 0), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (3, 1), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (3, 2), 100, 100, 8192, 64, MPENote::keyDown); + } + { + // pathological case: second note-on for same note should retrigger it. + UnitTestInstrument test; + test.setZoneLayout (testLayout); + test.noteOn (3, 0, MPEValue::from7BitInt (100)); + test.noteOn (3, 0, MPEValue::from7BitInt (60)); + expectEquals (test.getNumPlayingNotes(), 1); + expectNote (test.getNote (3, 0), 60, 60, 8192, 64, MPENote::keyDown); + } + } + + beginTest ("noteReleased after setZoneLayout"); + { + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + test.noteOn (4, 61, MPEValue::from7BitInt (100)); + expectEquals (test.getNumPlayingNotes(), 3); + expectEquals (test.noteReleasedCallCounter, 0); + + test.setZoneLayout (testLayout); + expectEquals (test.getNumPlayingNotes(), 0); + expectEquals (test.noteReleasedCallCounter, 3); + } + + beginTest ("releaseAllNotes"); + { + UnitTestInstrument test; + test.setZoneLayout (testLayout); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (4, 61, MPEValue::from7BitInt (100)); + test.noteOn (15, 62, MPEValue::from7BitInt (100)); + expectEquals (test.getNumPlayingNotes(), 3); + + test.releaseAllNotes(); + expectEquals (test.getNumPlayingNotes(), 0); + } + + beginTest ("sustainPedal"); + { + UnitTestInstrument test; + test.setZoneLayout (testLayout); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); // note in Zone 1 + test.noteOn (10, 60, MPEValue::from7BitInt (100)); // note in Zone 2 + + // sustain pedal on per-note channel shouldn't do anything. + test.sustainPedal (3, true); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + + + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.noteKeyStateChangedCallCounter, 0); + + // sustain pedal on non-zone channel shouldn't do anything either. + test.sustainPedal (1, true); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.noteKeyStateChangedCallCounter, 0); + + // sustain pedal on master channel should sustain notes on *that* zone. + test.sustainPedal (2, true); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDownAndSustained); + expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.noteKeyStateChangedCallCounter, 1); + + // release + test.sustainPedal (2, false); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.noteKeyStateChangedCallCounter, 2); + + // should also sustain new notes added after the press + test.sustainPedal (2, true); + expectEquals (test.noteKeyStateChangedCallCounter, 3); + test.noteOn (4, 51, MPEValue::from7BitInt (100)); + expectNote (test.getNote (4, 51), 100, 100, 8192, 64, MPENote::keyDownAndSustained); + expectEquals (test.noteKeyStateChangedCallCounter, 3); + + // ...but only if that sustain came on the master channel of that zone! + test.sustainPedal (11, true); + test.noteOn (11, 52, MPEValue::from7BitInt (100)); + expectNote (test.getNote (11, 52), 100, 100, 8192, 64, MPENote::keyDown); + test.noteOff (11, 52, MPEValue::from7BitInt (100)); + expectEquals (test.noteReleasedCallCounter, 1); + + // note-off should not turn off sustained notes inside the same zone + test.noteOff (3, 60, MPEValue::from7BitInt (100)); + test.noteOff (4, 51, MPEValue::from7BitInt (100)); + test.noteOff (10, 60, MPEValue::from7BitInt (100)); // not affected! + expectEquals (test.getNumPlayingNotes(), 2); + expectEquals (test.noteReleasedCallCounter, 2); + expectEquals (test.noteKeyStateChangedCallCounter, 5); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::sustained); + expectNote (test.getNote (4, 51), 100, 100, 8192, 64, MPENote::sustained); + + // notes should be turned off when pedal is released + test.sustainPedal (2, false); + expectEquals (test.getNumPlayingNotes(), 0); + expectEquals (test.noteReleasedCallCounter, 4); + } + + beginTest ("sostenutoPedal"); + { + UnitTestInstrument test; + test.setZoneLayout (testLayout); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); // note in Zone 1 + test.noteOn (10, 60, MPEValue::from7BitInt (100)); // note in Zone 2 + + // sostenuto pedal on per-note channel shouldn't do anything. + test.sostenutoPedal (3, true); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.noteKeyStateChangedCallCounter, 0); + + // sostenuto pedal on non-zone channel shouldn't do anything either. + test.sostenutoPedal (1, true); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.noteKeyStateChangedCallCounter, 0); + + // sostenuto pedal on master channel should sustain notes on *that* zone. + test.sostenutoPedal (2, true); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDownAndSustained); + expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.noteKeyStateChangedCallCounter, 1); + + // release + test.sostenutoPedal (2, false); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.noteKeyStateChangedCallCounter, 2); + + // should only sustain notes turned on *before* the press (difference to sustain pedal) + test.sostenutoPedal (2, true); + expectEquals (test.noteKeyStateChangedCallCounter, 3); + test.noteOn (4, 51, MPEValue::from7BitInt (100)); + expectEquals (test.getNumPlayingNotes(), 3); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDownAndSustained); + expectNote (test.getNote (4, 51), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.noteKeyStateChangedCallCounter, 3); + + // note-off should not turn off sustained notes inside the same zone, + // but only if they were turned on *before* the sostenuto pedal (difference to sustain pedal) + test.noteOff (3, 60, MPEValue::from7BitInt (100)); + test.noteOff (4, 51, MPEValue::from7BitInt (100)); + test.noteOff (10, 60, MPEValue::from7BitInt (100)); // not affected! + expectEquals (test.getNumPlayingNotes(), 1); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::sustained); + expectEquals (test.noteReleasedCallCounter, 2); + expectEquals (test.noteKeyStateChangedCallCounter, 4); + + // notes should be turned off when pedal is released + test.sustainPedal (2, false); + expectEquals (test.getNumPlayingNotes(), 0); + expectEquals (test.noteReleasedCallCounter, 3); + } + + beginTest ("getMostRecentNote"); + { + MPEInstrument test; + test.setZoneLayout (testLayout); + + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + + { + MPENote note = test.getMostRecentNote (2); + expect (! note.isValid()); + } + { + MPENote note = test.getMostRecentNote (3); + expect (note.isValid()); + expectEquals (int (note.midiChannel), 3); + expectEquals (int (note.initialNote), 61); + } + + test.sustainPedal (2, true); + test.noteOff (3, 61, MPEValue::from7BitInt (100)); + + { + MPENote note = test.getMostRecentNote (3); + expect (note.isValid()); + expectEquals (int (note.midiChannel), 3); + expectEquals (int (note.initialNote), 60); + } + + test.sustainPedal (2, false); + test.noteOff (3, 60, MPEValue::from7BitInt (100)); + + { + MPENote note = test.getMostRecentNote (3); + expect (! note.isValid()); + } + } + + beginTest ("getMostRecentNoteOtherThan"); + { + MPENote testNote (3, 60, + MPEValue::centreValue(), MPEValue::centreValue(), + MPEValue::centreValue(), MPEValue::centreValue()); + + { + // case 1: the note to exclude is not the most recent one. + + MPEInstrument test; + test.setZoneLayout (testLayout); + expect (! test.getMostRecentNoteOtherThan (testNote).isValid()); + + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + expect (! test.getMostRecentNoteOtherThan (testNote).isValid()); + + test.noteOn (4, 61, MPEValue::from7BitInt (100)); + expect (test.getMostRecentNoteOtherThan (testNote).isValid()); + expect (test.getMostRecentNoteOtherThan (testNote).midiChannel == 4); + expect (test.getMostRecentNoteOtherThan (testNote).initialNote == 61); + } + { + // case 2: the note to exclude is the most recent one. + + MPEInstrument test; + test.setZoneLayout (testLayout); + expect (! test.getMostRecentNoteOtherThan (testNote).isValid()); + + test.noteOn (4, 61, MPEValue::from7BitInt (100)); + expect (test.getMostRecentNoteOtherThan (testNote).isValid()); + expect (test.getMostRecentNoteOtherThan (testNote).midiChannel == 4); + expect (test.getMostRecentNoteOtherThan (testNote).initialNote == 61); + + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + expect (test.getMostRecentNoteOtherThan (testNote).isValid()); + expect (test.getMostRecentNoteOtherThan (testNote).midiChannel == 4); + expect (test.getMostRecentNoteOtherThan (testNote).initialNote == 61); + } + } + + beginTest ("pressure"); + { + { + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (4, 60, MPEValue::from7BitInt (100)); + test.noteOn (10, 60, MPEValue::from7BitInt (100)); + + // applying pressure on a per-note channel should modulate one note + test.pressure (3, MPEValue::from7BitInt (33)); + expectNote (test.getNote (3, 60), 100, 33, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.notePressureChangedCallCounter, 1); + + // applying pressure on a master channel should modulate all notes in this zone + test.pressure (2, MPEValue::from7BitInt (44)); + expectNote (test.getNote (3, 60), 100, 44, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (4, 60), 100, 44, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.notePressureChangedCallCounter, 3); + + // applying pressure on an unrelated channel should be ignored + test.pressure (1, MPEValue::from7BitInt (55)); + expectNote (test.getNote (3, 60), 100, 44, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (4, 60), 100, 44, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.notePressureChangedCallCounter, 3); + } + { + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + // two notes on same channel - only last added should be modulated + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + test.pressure (3, MPEValue::from7BitInt (66)); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (3, 61), 100, 66, 8192, 64, MPENote::keyDown); + expectEquals (test.notePressureChangedCallCounter, 1); + } + { + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + // edge case: two notes on same channel, one gets released, + // then the other should be modulated + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + test.noteOff (3, 61, MPEValue::from7BitInt (100)); + test.pressure (3, MPEValue::from7BitInt (77)); + expectEquals (test.getNumPlayingNotes(), 1); + expectNote (test.getNote (3, 60), 100, 77, 8192, 64, MPENote::keyDown); + expectEquals (test.notePressureChangedCallCounter, 1); + } + } + + beginTest ("pitchbend"); + { + { + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (4, 60, MPEValue::from7BitInt (100)); + test.noteOn (10, 60, MPEValue::from7BitInt (100)); + + // applying pitchbend on a per-note channel should modulate one note + test.pitchbend (3, MPEValue::from14BitInt (1111)); + expectNote (test.getNote (3, 60), 100, 100, 1111, 64, MPENote::keyDown); + expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.notePitchbendChangedCallCounter, 1); + + // applying pitchbend on a master channel should be ignored for the + // value of per-note pitchbend. Tests covering master pitchbend below. + // Note: noteChanged will be called anyway for notes in that zone + // because the total pitchbend for those notes has changed + test.pitchbend (2, MPEValue::from14BitInt (2222)); + expectNote (test.getNote (3, 60), 100, 100, 1111, 64, MPENote::keyDown); + expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.notePitchbendChangedCallCounter, 3); + + // applying pitchbend on an unrelated channel should do nothing. + test.pitchbend (1, MPEValue::from14BitInt (3333)); + expectNote (test.getNote (3, 60), 100, 100, 1111, 64, MPENote::keyDown); + expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.notePitchbendChangedCallCounter, 3); + } + { + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + // two notes on same channel - only last added should be bent + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + test.pitchbend (3, MPEValue::from14BitInt (4444)); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (3, 61), 100, 100, 4444, 64, MPENote::keyDown); + expectEquals (test.notePitchbendChangedCallCounter, 1); + } + { + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + // edge case: two notes on same channel, one gets released, + // then the other should be bent + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + test.noteOff (3, 61, MPEValue::from7BitInt (100)); + test.pitchbend (3, MPEValue::from14BitInt (5555)); + expectEquals (test.getNumPlayingNotes(), 1); + expectNote (test.getNote (3, 60), 100, 100, 5555, 64, MPENote::keyDown); + expectEquals (test.notePitchbendChangedCallCounter, 1); + } + { + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + // Richard's edge case: + // - press one note + // - press sustain (careful: must be sent on master channel) + // - release first note (is still sustained!) + // - press another note (happens to be on the same MIDI channel!) + // - pitchbend that other note + // - the first note should not be bent, only the second one. + + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.sustainPedal (2, true); + test.noteOff (3, 60, MPEValue::from7BitInt (64)); + expectEquals (test.getNumPlayingNotes(), 1); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::sustained); + expectEquals (test.noteKeyStateChangedCallCounter, 2); + + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + test.pitchbend (3, MPEValue::from14BitInt (6666)); + expectEquals (test.getNumPlayingNotes(), 2); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::sustained); + expectNote (test.getNote (3, 61), 100, 100, 6666, 64, MPENote::keyDownAndSustained); + expectEquals (test.notePitchbendChangedCallCounter, 1); + } + { + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + // Zsolt's edge case: + // - press one note + // - modulate pitchbend or timbre + // - release the note + // - press same note again without sending a pitchbend or timbre message before the note-on + // - the note should be turned on with a default value for pitchbend/timbre, + // and *not* the last value received on channel. + + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.pitchbend (3, MPEValue::from14BitInt (5555)); + expectNote (test.getNote (3, 60), 100, 100, 5555, 64, MPENote::keyDown); + + test.noteOff (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + } + { + // applying per-note pitchbend should set the note's totalPitchbendInSemitones + // correctly depending on the per-note pitchbend range of the zone. + UnitTestInstrument test; + + MPEZoneLayout layout = testLayout; + test.setZoneLayout (layout); // default should be +/- 48 semitones + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.pitchbend (3, MPEValue::from14BitInt (4096)); + expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -24.0, 0.01); + + layout.getZoneByIndex (0)->setPerNotePitchbendRange (96); + test.setZoneLayout (layout); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.pitchbend (3, MPEValue::from14BitInt (0)); // -max + expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -96.0, 0.01); + + layout.getZoneByIndex (0)->setPerNotePitchbendRange (1); + test.setZoneLayout (layout); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.pitchbend (3, MPEValue::from14BitInt (16383)); // +max + expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 1.0, 0.01); + + layout.getZoneByIndex (0)->setPerNotePitchbendRange (0); // pitchbendrange = 0 --> no pitchbend at all + test.setZoneLayout (layout); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.pitchbend (3, MPEValue::from14BitInt (12345)); + expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 0.0, 0.01); + } + { + // applying master pitchbend should set the note's totalPitchbendInSemitones + // correctly depending on the master pitchbend range of the zone. + UnitTestInstrument test; + + MPEZoneLayout layout = testLayout; + test.setZoneLayout (layout); // default should be +/- 2 semitones + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.pitchbend (2, MPEValue::from14BitInt (4096)); //halfway between -max and centre + expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -1.0, 0.01); + + layout.getZoneByIndex (0)->setMasterPitchbendRange (96); + test.setZoneLayout (layout); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.pitchbend (2, MPEValue::from14BitInt (0)); // -max + expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -96.0, 0.01); + + layout.getZoneByIndex (0)->setMasterPitchbendRange (1); + test.setZoneLayout (layout); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.pitchbend (2, MPEValue::from14BitInt (16383)); // +max + expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 1.0, 0.01); + + layout.getZoneByIndex (0)->setMasterPitchbendRange (0); // pitchbendrange = 0 --> no pitchbend at all + test.setZoneLayout (layout); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.pitchbend (2, MPEValue::from14BitInt (12345)); + expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 0.0, 0.01); + } + { + // applying both per-note and master pitchbend simultaneously should set + // the note's totalPitchbendInSemitones to the sum of both, correctly + // weighted with the per-note and master pitchbend range, respectively. + UnitTestInstrument test; + + MPEZoneLayout layout = testLayout; + layout.getZoneByIndex (0)->setPerNotePitchbendRange (12); + layout.getZoneByIndex (0)->setMasterPitchbendRange (1); + test.setZoneLayout (layout); + + test.pitchbend (2, MPEValue::from14BitInt (4096)); // master pitchbend 0.5 semitones down + test.pitchbend (3, MPEValue::from14BitInt (0)); // per-note pitchbend 12 semitones down + // additionally, note should react to both pitchbend messages + // correctly even if they arrived before the note-on. + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -12.5, 0.01); + } + } + + beginTest ("timbre"); + { + { + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (4, 60, MPEValue::from7BitInt (100)); + test.noteOn (10, 60, MPEValue::from7BitInt (100)); + + // modulating timbre on a per-note channel should modulate one note + test.timbre (3, MPEValue::from7BitInt (33)); + expectNote (test.getNote (3, 60), 100, 100, 8192, 33, MPENote::keyDown); + expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.noteTimbreChangedCallCounter, 1); + + // modulating timbre on a master channel should modulate all notes in this zone + test.timbre (2, MPEValue::from7BitInt (44)); + expectNote (test.getNote (3, 60), 100, 100, 8192, 44, MPENote::keyDown); + expectNote (test.getNote (4, 60), 100, 100, 8192, 44, MPENote::keyDown); + expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.noteTimbreChangedCallCounter, 3); + + // modulating timbre on an unrelated channel should be ignored + test.timbre (1, MPEValue::from7BitInt (55)); + expectNote (test.getNote (3, 60), 100, 100, 8192, 44, MPENote::keyDown); + expectNote (test.getNote (4, 60), 100, 100, 8192, 44, MPENote::keyDown); + expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.noteTimbreChangedCallCounter, 3); + } + { + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + // two notes on same channel - only last added should be modulated + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + test.timbre (3, MPEValue::from7BitInt (66)); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (3, 61), 100, 100, 8192, 66, MPENote::keyDown); + expectEquals (test.noteTimbreChangedCallCounter, 1); + } + { + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + // edge case: two notes on same channel, one gets released, + // then the other should be modulated + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + test.noteOff (3, 61, MPEValue::from7BitInt (100)); + test.timbre (3, MPEValue::from7BitInt (77)); + expectEquals (test.getNumPlayingNotes(), 1); + expectNote (test.getNote (3, 60), 100, 100, 8192, 77, MPENote::keyDown); + expectEquals (test.noteTimbreChangedCallCounter, 1); + } + { + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + // Zsolt's edge case for timbre + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.timbre (3, MPEValue::from7BitInt (42)); + expectNote (test.getNote (3, 60), 100, 100, 8192, 42, MPENote::keyDown); + + test.noteOff (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + } + } + + beginTest ("setPressureTrackingMode"); + { + { + // last note played (= default) + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + test.setPressureTrackingMode (MPEInstrument::lastNotePlayedOnChannel); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 62, MPEValue::from7BitInt (100)); + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + test.pressure (3, MPEValue::from7BitInt (99)); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (3, 61), 100, 99, 8192, 64, MPENote::keyDown); + expectEquals (test.notePressureChangedCallCounter, 1); + } + { + // lowest note + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + test.setPressureTrackingMode (MPEInstrument::lowestNoteOnChannel); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 62, MPEValue::from7BitInt (100)); + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + test.pressure (3, MPEValue::from7BitInt (99)); + expectNote (test.getNote (3, 60), 100, 99, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.notePressureChangedCallCounter, 1); + } + { + // highest note + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + test.setPressureTrackingMode (MPEInstrument::highestNoteOnChannel); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 62, MPEValue::from7BitInt (100)); + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + test.pressure (3, MPEValue::from7BitInt (99)); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (3, 62), 100, 99, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.notePressureChangedCallCounter, 1); + } + { + // all notes + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + test.setPressureTrackingMode (MPEInstrument::allNotesOnChannel); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 62, MPEValue::from7BitInt (100)); + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + test.pressure (3, MPEValue::from7BitInt (99)); + expectNote (test.getNote (3, 60), 100, 99, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (3, 62), 100, 99, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (3, 61), 100, 99, 8192, 64, MPENote::keyDown); + expectEquals (test.notePressureChangedCallCounter, 3); + } + } + + beginTest ("setPitchbendTrackingMode"); + { + { + // last note played (= default) + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + test.setPitchbendTrackingMode (MPEInstrument::lastNotePlayedOnChannel); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 62, MPEValue::from7BitInt (100)); + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + test.pitchbend (3, MPEValue::from14BitInt (9999)); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (3, 61), 100, 100, 9999, 64, MPENote::keyDown); + expectEquals (test.notePitchbendChangedCallCounter, 1); + } + { + // lowest note + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + test.setPitchbendTrackingMode (MPEInstrument::lowestNoteOnChannel); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 62, MPEValue::from7BitInt (100)); + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + test.pitchbend (3, MPEValue::from14BitInt (9999)); + expectNote (test.getNote (3, 60), 100, 100, 9999, 64, MPENote::keyDown); + expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.notePitchbendChangedCallCounter, 1); + } + { + // highest note + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + test.setPitchbendTrackingMode (MPEInstrument::highestNoteOnChannel); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 62, MPEValue::from7BitInt (100)); + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + test.pitchbend (3, MPEValue::from14BitInt (9999)); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (3, 62), 100, 100, 9999, 64, MPENote::keyDown); + expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.notePitchbendChangedCallCounter, 1); + } + { + // all notes + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + test.setPitchbendTrackingMode (MPEInstrument::allNotesOnChannel); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 62, MPEValue::from7BitInt (100)); + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + test.pitchbend (3, MPEValue::from14BitInt (9999)); + expectNote (test.getNote (3, 60), 100, 100, 9999, 64, MPENote::keyDown); + expectNote (test.getNote (3, 62), 100, 100, 9999, 64, MPENote::keyDown); + expectNote (test.getNote (3, 61), 100, 100, 9999, 64, MPENote::keyDown); + expectEquals (test.notePitchbendChangedCallCounter, 3); + } + } + + beginTest ("setTimbreTrackingMode"); + { + { + // last note played (= default) + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + test.setTimbreTrackingMode (MPEInstrument::lastNotePlayedOnChannel); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 62, MPEValue::from7BitInt (100)); + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + test.timbre (3, MPEValue::from7BitInt (99)); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (3, 61), 100, 100, 8192, 99, MPENote::keyDown); + expectEquals (test.noteTimbreChangedCallCounter, 1); + } + { + // lowest note + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + test.setTimbreTrackingMode (MPEInstrument::lowestNoteOnChannel); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 62, MPEValue::from7BitInt (100)); + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + test.timbre (3, MPEValue::from7BitInt (99)); + expectNote (test.getNote (3, 60), 100, 100, 8192, 99, MPENote::keyDown); + expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.noteTimbreChangedCallCounter, 1); + } + { + // highest note + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + test.setTimbreTrackingMode (MPEInstrument::highestNoteOnChannel); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 62, MPEValue::from7BitInt (100)); + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + test.timbre (3, MPEValue::from7BitInt (99)); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (3, 62), 100, 100, 8192, 99, MPENote::keyDown); + expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown); + expectEquals (test.noteTimbreChangedCallCounter, 1); + } + { + // all notes + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + test.setTimbreTrackingMode (MPEInstrument::allNotesOnChannel); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 62, MPEValue::from7BitInt (100)); + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + test.timbre (3, MPEValue::from7BitInt (99)); + expectNote (test.getNote (3, 60), 100, 100, 8192, 99, MPENote::keyDown); + expectNote (test.getNote (3, 62), 100, 100, 8192, 99, MPENote::keyDown); + expectNote (test.getNote (3, 61), 100, 100, 8192, 99, MPENote::keyDown); + expectEquals (test.noteTimbreChangedCallCounter, 3); + } + } + + beginTest ("processNextMidiEvent"); + { + UnitTestInstrument test; + + // note on should trigger noteOn method call + + test.processNextMidiEvent (MidiMessage::noteOn (3, 42, uint8 (92))); + expectEquals (test.noteOnCallCounter, 1); + expectEquals (test.lastMidiChannelReceived, 3); + expectEquals (test.lastMidiNoteNumberReceived, 42); + expectEquals (test.lastMPEValueReceived.as7BitInt(), 92); + + // note off should trigger noteOff method call + + test.processNextMidiEvent (MidiMessage::noteOff (4, 12, uint8 (33))); + expectEquals (test.noteOffCallCounter, 1); + expectEquals (test.lastMidiChannelReceived, 4); + expectEquals (test.lastMidiNoteNumberReceived, 12); + expectEquals (test.lastMPEValueReceived.as7BitInt(), 33); + + // note on with velocity = 0 should trigger noteOff method call + // with a note off velocity of 64 (centre value) + + test.processNextMidiEvent (MidiMessage::noteOn (5, 11, uint8 (0))); + expectEquals (test.noteOffCallCounter, 2); + expectEquals (test.lastMidiChannelReceived, 5); + expectEquals (test.lastMidiNoteNumberReceived, 11); + expectEquals (test.lastMPEValueReceived.as7BitInt(), 64); + + // pitchwheel message should trigger pitchbend method call + + test.processNextMidiEvent (MidiMessage::pitchWheel (1, 3333)); + expectEquals (test.pitchbendCallCounter, 1); + expectEquals (test.lastMidiChannelReceived, 1); + expectEquals (test.lastMPEValueReceived.as14BitInt(), 3333); + + // pressure using channel pressure message (7-bit value) should + // trigger pressure method call + + test.processNextMidiEvent (MidiMessage::channelPressureChange (10, 35)); + expectEquals (test.pressureCallCounter, 1); + expectEquals (test.lastMidiChannelReceived, 10); + expectEquals (test.lastMPEValueReceived.as7BitInt(), 35); + + // pressure using 14-bit value over CC70 and CC102 should trigger + // pressure method call after the MSB is sent + + // a) sending only the MSB + test.processNextMidiEvent (MidiMessage::controllerEvent (3, 70, 120)); + expectEquals (test.pressureCallCounter, 2); + expectEquals (test.lastMidiChannelReceived, 3); + expectEquals (test.lastMPEValueReceived.as7BitInt(), 120); + + // b) sending LSB and MSB (only the MSB should trigger the call) - per MIDI channel! + test.processNextMidiEvent (MidiMessage::controllerEvent (4, 102, 121)); + expectEquals (test.pressureCallCounter, 2); + test.processNextMidiEvent (MidiMessage::controllerEvent (5, 102, 122)); + expectEquals (test.pressureCallCounter, 2); + test.processNextMidiEvent (MidiMessage::controllerEvent (4, 70, 123)); + expectEquals (test.pressureCallCounter, 3); + expectEquals (test.lastMidiChannelReceived, 4); + expectEquals (test.lastMPEValueReceived.as14BitInt(), 121 + (123 << 7)); + test.processNextMidiEvent (MidiMessage::controllerEvent (5, 70, 124)); + expectEquals (test.pressureCallCounter, 4); + expectEquals (test.lastMidiChannelReceived, 5); + expectEquals (test.lastMPEValueReceived.as14BitInt(), 122 + (124 << 7)); + test.processNextMidiEvent (MidiMessage::controllerEvent (5, 70, 64)); + expectEquals (test.pressureCallCounter, 5); + expectEquals (test.lastMidiChannelReceived, 5); + expectEquals (test.lastMPEValueReceived.as7BitInt(), 64); + + // same for timbre 14-bit value over CC74 and CC106 + test.processNextMidiEvent (MidiMessage::controllerEvent (3, 74, 120)); + expectEquals (test.timbreCallCounter, 1); + expectEquals (test.lastMidiChannelReceived, 3); + expectEquals (test.lastMPEValueReceived.as7BitInt(), 120); + test.processNextMidiEvent (MidiMessage::controllerEvent (4, 106, 121)); + expectEquals (test.timbreCallCounter, 1); + test.processNextMidiEvent (MidiMessage::controllerEvent (5, 106, 122)); + expectEquals (test.timbreCallCounter, 1); + test.processNextMidiEvent (MidiMessage::controllerEvent (4, 74, 123)); + expectEquals (test.timbreCallCounter, 2); + expectEquals (test.lastMidiChannelReceived, 4); + expectEquals (test.lastMPEValueReceived.as14BitInt(), 121 + (123 << 7)); + test.processNextMidiEvent (MidiMessage::controllerEvent (5, 74, 124)); + expectEquals (test.timbreCallCounter, 3); + expectEquals (test.lastMidiChannelReceived, 5); + expectEquals (test.lastMPEValueReceived.as14BitInt(), 122 + (124 << 7)); + test.processNextMidiEvent (MidiMessage::controllerEvent (5, 74, 64)); + expectEquals (test.timbreCallCounter, 4); + expectEquals (test.lastMidiChannelReceived, 5); + expectEquals (test.lastMPEValueReceived.as7BitInt(), 64); + + // sustain pedal message (CC64) should trigger sustainPedal method call + test.processNextMidiEvent (MidiMessage::controllerEvent (1, 64, 127)); + expectEquals (test.sustainPedalCallCounter, 1); + expectEquals (test.lastMidiChannelReceived, 1); + expect (test.lastSustainPedalValueReceived); + test.processNextMidiEvent (MidiMessage::controllerEvent (16, 64, 0)); + expectEquals (test.sustainPedalCallCounter, 2); + expectEquals (test.lastMidiChannelReceived, 16); + expect (! test.lastSustainPedalValueReceived); + + // sostenuto pedal message (CC66) should trigger sostenutoPedal method call + test.processNextMidiEvent (MidiMessage::controllerEvent (1, 66, 127)); + expectEquals (test.sostenutoPedalCallCounter, 1); + expectEquals (test.lastMidiChannelReceived, 1); + expect (test.lastSostenutoPedalValueReceived); + test.processNextMidiEvent (MidiMessage::controllerEvent (16, 66, 0)); + expectEquals (test.sostenutoPedalCallCounter, 2); + expectEquals (test.lastMidiChannelReceived, 16); + expect (! test.lastSostenutoPedalValueReceived); + } + { + // MIDI messages modifying the zone layout should be correctly + // forwarded to the internal zone layout and modify it. + // (testing the actual logic of the zone layout is done in the + // MPEZoneLayout unit tests) + MPEInstrument test; + + MidiBuffer buffer; + buffer.addEvents (MPEMessages::addZone (MPEZone (2, 5)), 0, -1, 0); + buffer.addEvents (MPEMessages::addZone (MPEZone (9, 6)), 0, -1, 0); + + MidiBuffer::Iterator iter (buffer); + MidiMessage message; + int samplePosition; // not actually used, so no need to initialise. + + while (iter.getNextEvent (message, samplePosition)) + test.processNextMidiEvent (message); + + expectEquals (test.getZoneLayout().getNumZones(), 2); + expectEquals (test.getZoneLayout().getZoneByIndex (0)->getMasterChannel(), 2); + expectEquals (test.getZoneLayout().getZoneByIndex (0)->getNumNoteChannels(), 5); + expectEquals (test.getZoneLayout().getZoneByIndex (1)->getMasterChannel(), 9); + expectEquals (test.getZoneLayout().getZoneByIndex (1)->getNumNoteChannels(), 6); + } + + beginTest ("MIDI all notes off"); + { + UnitTestInstrument test; + test.setZoneLayout (testLayout); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (4, 61, MPEValue::from7BitInt (100)); + test.noteOn (15, 62, MPEValue::from7BitInt (100)); + test.noteOn (15, 63, MPEValue::from7BitInt (100)); + expectEquals (test.getNumPlayingNotes(), 4); + + // on note channel: ignore. + test.processNextMidiEvent (MidiMessage::allNotesOff (3)); + expectEquals (test.getNumPlayingNotes(), 4); + + // on unused channel: ignore. + test.processNextMidiEvent (MidiMessage::allNotesOff (1)); + expectEquals (test.getNumPlayingNotes(), 4); + + // on master channel: release notes in that zone only. + test.processNextMidiEvent (MidiMessage::allNotesOff (2)); + expectEquals (test.getNumPlayingNotes(), 2); + test.processNextMidiEvent (MidiMessage::allNotesOff (9)); + expectEquals (test.getNumPlayingNotes(), 0); + } + + beginTest ("MIDI all notes off (legacy mode)"); + { + UnitTestInstrument test; + test.enableLegacyMode(); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.noteOn (4, 61, MPEValue::from7BitInt (100)); + test.noteOn (15, 62, MPEValue::from7BitInt (100)); + test.noteOn (15, 63, MPEValue::from7BitInt (100)); + expectEquals (test.getNumPlayingNotes(), 4); + + test.processNextMidiEvent (MidiMessage::allNotesOff (3)); + expectEquals (test.getNumPlayingNotes(), 3); + + test.processNextMidiEvent (MidiMessage::allNotesOff (15)); + expectEquals (test.getNumPlayingNotes(), 1); + + test.processNextMidiEvent (MidiMessage::allNotesOff (4)); + expectEquals (test.getNumPlayingNotes(), 0); + } + + beginTest ("default getInitial...ForNoteOn"); + { + MPEInstrument test; + test.setZoneLayout (testLayout); + + test.pitchbend (3, MPEValue::from14BitInt (3333)); // use for next note-on on ch. 3 + test.pitchbend (2, MPEValue::from14BitInt (4444)); // ignore + test.pitchbend (2, MPEValue::from14BitInt (5555)); // ignore + + test.timbre (3, MPEValue::from7BitInt (66)); // use for next note-on on ch. 3 + test.timbre (2, MPEValue::from7BitInt (77)); // ignore + test.timbre (2, MPEValue::from7BitInt (88)); // ignore + + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + + expectNote (test.getMostRecentNote (3), 100, 100, 3333, 66, MPENote::keyDown); + } + + beginTest ("overriding getInitial...ForNoteOn"); + { + CustomInitialValuesTest<33, 4444, 55> test; + test.setZoneLayout (testLayout); + + test.noteOn (3, 61, MPEValue::from7BitInt (100)); + expectNote (test.getMostRecentNote (3), 100, 33, 4444, 55, MPENote::keyDown); + } + + beginTest ("Legacy mode"); + { + { + // basic check + MPEInstrument test; + expect (! test.isLegacyModeEnabled()); + + test.setZoneLayout (testLayout); + expect (! test.isLegacyModeEnabled()); + + test.enableLegacyMode(); + expect (test.isLegacyModeEnabled()); + + test.setZoneLayout (testLayout); + expect (! test.isLegacyModeEnabled()); + } + { + // constructor w/o default arguments + MPEInstrument test; + test.enableLegacyMode (0, Range (1, 11)); + expectEquals (test.getLegacyModePitchbendRange(), 0); + expect (test.getLegacyModeChannelRange() == Range (1, 11)); + } + { + // getters and setters + MPEInstrument test; + test.enableLegacyMode(); + + expectEquals (test.getLegacyModePitchbendRange(), 2); + expect (test.getLegacyModeChannelRange() == Range (1, 17)); + + test.setLegacyModePitchbendRange (96); + expectEquals (test.getLegacyModePitchbendRange(), 96); + + test.setLegacyModeChannelRange (Range (10, 12)); + expect (test.getLegacyModeChannelRange() == Range (10, 12)); + } + { + // note on should trigger notes on all 16 channels + + UnitTestInstrument test; + test.enableLegacyMode(); + + test.noteOn (1, 60, MPEValue::from7BitInt (100)); + test.noteOn (2, 60, MPEValue::from7BitInt (100)); + test.noteOn (15, 60, MPEValue::from7BitInt (100)); + test.noteOn (16, 60, MPEValue::from7BitInt (100)); + expectEquals (test.getNumPlayingNotes(), 4); + + // polyphonic modulation should work across all 16 channels + + test.pitchbend (1, MPEValue::from14BitInt (9999)); + test.pressure (2, MPEValue::from7BitInt (88)); + test.timbre (15, MPEValue::from7BitInt (77)); + + expectNote (test.getNote (1, 60), 100, 100, 9999, 64, MPENote::keyDown); + expectNote (test.getNote (2, 60), 100, 88, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (15, 60), 100, 100, 8192, 77, MPENote::keyDown); + expectNote (test.getNote (16, 60), 100, 100, 8192, 64, MPENote::keyDown); + + // note off should work in legacy mode + + test.noteOff (15, 60, MPEValue::from7BitInt (0)); + test.noteOff (1, 60, MPEValue::from7BitInt (0)); + test.noteOff (2, 60, MPEValue::from7BitInt (0)); + test.noteOff (16, 60, MPEValue::from7BitInt (0)); + expectEquals (test.getNumPlayingNotes(), 0); + } + { + // legacy mode w/ custom channel range: note on should trigger notes only within range + + UnitTestInstrument test; + test.enableLegacyMode (2, Range (3, 8)); // channels 3-7 + + test.noteOn (1, 60, MPEValue::from7BitInt (100)); + test.noteOn (2, 60, MPEValue::from7BitInt (100)); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); // should trigger + test.noteOn (4, 60, MPEValue::from7BitInt (100)); // should trigger + test.noteOn (6, 60, MPEValue::from7BitInt (100)); // should trigger + test.noteOn (7, 60, MPEValue::from7BitInt (100)); // should trigger + test.noteOn (8, 60, MPEValue::from7BitInt (100)); + test.noteOn (16, 60, MPEValue::from7BitInt (100)); + + expectEquals (test.getNumPlayingNotes(), 4); + expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (6, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (7, 60), 100, 100, 8192, 64, MPENote::keyDown); + } + { + // tracking mode in legacy mode + { + UnitTestInstrument test; + test.enableLegacyMode(); + + test.setPitchbendTrackingMode (MPEInstrument::lastNotePlayedOnChannel); + test.noteOn (1, 60, MPEValue::from7BitInt (100)); + test.noteOn (1, 62, MPEValue::from7BitInt (100)); + test.noteOn (1, 61, MPEValue::from7BitInt (100)); + test.pitchbend (1, MPEValue::from14BitInt (9999)); + expectNote (test.getNote (1, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (1, 61), 100, 100, 9999, 64, MPENote::keyDown); + expectNote (test.getNote (1, 62), 100, 100, 8192, 64, MPENote::keyDown); + } + { + UnitTestInstrument test; + test.enableLegacyMode(); + + test.setPitchbendTrackingMode (MPEInstrument::lowestNoteOnChannel); + test.noteOn (1, 60, MPEValue::from7BitInt (100)); + test.noteOn (1, 62, MPEValue::from7BitInt (100)); + test.noteOn (1, 61, MPEValue::from7BitInt (100)); + test.pitchbend (1, MPEValue::from14BitInt (9999)); + expectNote (test.getNote (1, 60), 100, 100, 9999, 64, MPENote::keyDown); + expectNote (test.getNote (1, 61), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (1, 62), 100, 100, 8192, 64, MPENote::keyDown); + } + { + UnitTestInstrument test; + test.enableLegacyMode(); + + test.setPitchbendTrackingMode (MPEInstrument::highestNoteOnChannel); + test.noteOn (1, 60, MPEValue::from7BitInt (100)); + test.noteOn (1, 62, MPEValue::from7BitInt (100)); + test.noteOn (1, 61, MPEValue::from7BitInt (100)); + test.pitchbend (1, MPEValue::from14BitInt (9999)); + expectNote (test.getNote (1, 60), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (1, 61), 100, 100, 8192, 64, MPENote::keyDown); + expectNote (test.getNote (1, 62), 100, 100, 9999, 64, MPENote::keyDown); + } + { + UnitTestInstrument test; + test.enableLegacyMode(); + + test.setPitchbendTrackingMode (MPEInstrument::allNotesOnChannel); + test.noteOn (1, 60, MPEValue::from7BitInt (100)); + test.noteOn (1, 62, MPEValue::from7BitInt (100)); + test.noteOn (1, 61, MPEValue::from7BitInt (100)); + test.pitchbend (1, MPEValue::from14BitInt (9999)); + expectNote (test.getNote (1, 60), 100, 100, 9999, 64, MPENote::keyDown); + expectNote (test.getNote (1, 61), 100, 100, 9999, 64, MPENote::keyDown); + expectNote (test.getNote (1, 62), 100, 100, 9999, 64, MPENote::keyDown); + } + } + { + // custom pitchbend range in legacy mode. + UnitTestInstrument test; + test.enableLegacyMode (11); + + test.pitchbend (1, MPEValue::from14BitInt (4096)); + test.noteOn (1, 60, MPEValue::from7BitInt (100)); + expectDoubleWithinRelativeError (test.getMostRecentNote (1).totalPitchbendInSemitones, -5.5, 0.01); + } + { + // sustain pedal should be per channel in legacy mode. + UnitTestInstrument test; + test.enableLegacyMode(); + + test.sustainPedal (1, true); + test.noteOn (2, 61, MPEValue::from7BitInt (100)); + test.noteOff (2, 61, MPEValue::from7BitInt (100)); + test.noteOn (1, 60, MPEValue::from7BitInt (100)); + test.noteOff (1, 60, MPEValue::from7BitInt (100)); + + expectEquals (test.getNumPlayingNotes(), 1); + expectNote (test.getNote (1, 60), 100, 100, 8192, 64, MPENote::sustained); + + test.sustainPedal (1, false); + expectEquals (test.getNumPlayingNotes(), 0); + + test.noteOn (2, 61, MPEValue::from7BitInt (100)); + test.sustainPedal (1, true); + test.noteOff (2, 61, MPEValue::from7BitInt (100)); + expectEquals (test.getNumPlayingNotes(), 0); + + } + { + // sostenuto pedal should be per channel in legacy mode. + UnitTestInstrument test; + test.enableLegacyMode(); + + test.noteOn (1, 60, MPEValue::from7BitInt (100)); + test.sostenutoPedal (1, true); + test.noteOff (1, 60, MPEValue::from7BitInt (100)); + test.noteOn (2, 61, MPEValue::from7BitInt (100)); + test.noteOff (2, 61, MPEValue::from7BitInt (100)); + + expectEquals (test.getNumPlayingNotes(), 1); + expectNote (test.getNote (1, 60), 100, 100, 8192, 64, MPENote::sustained); + + test.sostenutoPedal (1, false); + expectEquals (test.getNumPlayingNotes(), 0); + + test.noteOn (2, 61, MPEValue::from7BitInt (100)); + test.sostenutoPedal (1, true); + test.noteOff (2, 61, MPEValue::from7BitInt (100)); + expectEquals (test.getNumPlayingNotes(), 0); + } + { + // all notes released when switching layout + UnitTestInstrument test; + test.setZoneLayout (testLayout); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + expectEquals (test.getNumPlayingNotes(), 1); + + test.enableLegacyMode(); + expectEquals (test.getNumPlayingNotes(), 0); + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + expectEquals (test.getNumPlayingNotes(), 1); + + test.setZoneLayout (testLayout); + expectEquals (test.getNumPlayingNotes(), 0); + } + } + } + +private: + //========================================================================== + /* This mock class is used for unit testing whether the methods of + MPEInstrument are called correctly. + */ + class UnitTestInstrument : public MPEInstrument, + private MPEInstrument::Listener + { + typedef MPEInstrument Base; + public: + UnitTestInstrument() + : noteOnCallCounter (0), noteOffCallCounter (0), pitchbendCallCounter (0), + pressureCallCounter (0), timbreCallCounter (0), sustainPedalCallCounter (0), + sostenutoPedalCallCounter (0), noteAddedCallCounter (0), notePressureChangedCallCounter (0), + notePitchbendChangedCallCounter (0), noteTimbreChangedCallCounter (0), + noteKeyStateChangedCallCounter (0), noteReleasedCallCounter (0), + lastMidiChannelReceived (-1), lastMidiNoteNumberReceived (-1), + lastSustainPedalValueReceived (false), lastSostenutoPedalValueReceived (false) + { + addListener (this); + } + + void noteOn (int midiChannel, int midiNoteNumber, MPEValue midiNoteOnVelocity) override + { + Base::noteOn (midiChannel, midiNoteNumber, midiNoteOnVelocity); + + noteOnCallCounter++; + lastMidiChannelReceived = midiChannel; + lastMidiNoteNumberReceived = midiNoteNumber; + lastMPEValueReceived = midiNoteOnVelocity; + } + + void noteOff (int midiChannel, int midiNoteNumber, MPEValue midiNoteOffVelocity) override + { + Base::noteOff (midiChannel, midiNoteNumber, midiNoteOffVelocity); + + noteOffCallCounter++; + lastMidiChannelReceived = midiChannel; + lastMidiNoteNumberReceived = midiNoteNumber; + lastMPEValueReceived = midiNoteOffVelocity; + } + + void pitchbend (int midiChannel, MPEValue value) override + { + Base::pitchbend (midiChannel, value); + + pitchbendCallCounter++; + lastMidiChannelReceived = midiChannel; + lastMPEValueReceived = value; + } + + void pressure (int midiChannel, MPEValue value) override + { + Base::pressure (midiChannel, value); + + pressureCallCounter++; + lastMidiChannelReceived = midiChannel; + lastMPEValueReceived = value; + } + + void timbre (int midiChannel, MPEValue value) override + { + Base::timbre (midiChannel, value); + + timbreCallCounter++; + lastMidiChannelReceived = midiChannel; + lastMPEValueReceived = value; + } + + void sustainPedal (int midiChannel, bool value) override + { + Base::sustainPedal (midiChannel, value); + + sustainPedalCallCounter++; + lastMidiChannelReceived = midiChannel; + lastSustainPedalValueReceived = value; + } + + void sostenutoPedal (int midiChannel, bool value) override + { + Base::sostenutoPedal (midiChannel, value); + + sostenutoPedalCallCounter++; + lastMidiChannelReceived = midiChannel; + lastSostenutoPedalValueReceived = value; + } + + int noteOnCallCounter, noteOffCallCounter, pitchbendCallCounter, + pressureCallCounter, timbreCallCounter, sustainPedalCallCounter, + sostenutoPedalCallCounter, noteAddedCallCounter, + notePressureChangedCallCounter, notePitchbendChangedCallCounter, + noteTimbreChangedCallCounter, noteKeyStateChangedCallCounter, + noteReleasedCallCounter, lastMidiChannelReceived, lastMidiNoteNumberReceived; + + bool lastSustainPedalValueReceived, lastSostenutoPedalValueReceived; + MPEValue lastMPEValueReceived; + ScopedPointer lastNoteFinished; + + private: + //====================================================================== + void noteAdded (MPENote) override { noteAddedCallCounter++; } + + void notePressureChanged (MPENote) override { notePressureChangedCallCounter++; } + void notePitchbendChanged (MPENote) override { notePitchbendChangedCallCounter++; } + void noteTimbreChanged (MPENote) override { noteTimbreChangedCallCounter++; } + void noteKeyStateChanged (MPENote) override { noteKeyStateChangedCallCounter++; } + + void noteReleased (MPENote finishedNote) override + { + noteReleasedCallCounter++; + lastNoteFinished = new MPENote (finishedNote); + } + }; + + //========================================================================== + template + class CustomInitialValuesTest : public MPEInstrument + { + MPEValue getInitialPitchbendForNoteOn (int, int, MPEValue) const override + { + return MPEValue::from14BitInt (initial14BitPitchbend); + } + + MPEValue getInitialPressureForNoteOn (int, int, MPEValue) const override + { + return MPEValue::from7BitInt (initial7BitPressure); + } + + MPEValue getInitialTimbreForNoteOn (int, int, MPEValue) const override + { + return MPEValue::from7BitInt (initial7BitTimbre); + } + }; + + //========================================================================== + void expectNote (MPENote noteToTest, + int noteOnVelocity7Bit, + int pressure7Bit, + int pitchbend14Bit, + int timbre7Bit, + MPENote::KeyState keyState) + { + expect (noteToTest.isValid()); + expectEquals (noteToTest.noteOnVelocity.as7BitInt(), noteOnVelocity7Bit); + expectEquals (noteToTest.pressure.as7BitInt(), pressure7Bit); + expectEquals (noteToTest.pitchbend.as14BitInt(), pitchbend14Bit); + expectEquals (noteToTest.timbre.as7BitInt(),timbre7Bit); + expect (noteToTest.keyState == keyState); + } + + void expectHasFinishedNote (const UnitTestInstrument& test, + int channel, int noteNumber, int noteOffVelocity7Bit) + { + expect (test.lastNoteFinished != nullptr); + expectEquals (int (test.lastNoteFinished->midiChannel), channel); + expectEquals (int (test.lastNoteFinished->initialNote), noteNumber); + expectEquals (test.lastNoteFinished->noteOffVelocity.as7BitInt(), noteOffVelocity7Bit); + expect (test.lastNoteFinished->keyState == MPENote::off); + } + + void expectDoubleWithinRelativeError (double actual, double expected, double maxRelativeError) + { + const double maxAbsoluteError = jmax (1.0, std::fabs (expected)) * maxRelativeError; + expect (std::fabs (expected - actual) < maxAbsoluteError); + } + + //========================================================================== + MPEZoneLayout testLayout; +}; + +static MPEInstrumentTests MPEInstrumentUnitTests; + +#endif // JUCE_UNIT_TESTS diff --git a/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.h b/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.h new file mode 100644 index 000000000..db7257098 --- /dev/null +++ b/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.h @@ -0,0 +1,414 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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_MPEINSTRUMENT_H_INCLUDED +#define JUCE_MPEINSTRUMENT_H_INCLUDED + + +//============================================================================== +/* + This class represents an instrument handling MPE. + + It has an MPE zone layout and maintans a state of currently + active (playing) notes and the values of their dimensions of expression. + + You can trigger and modulate notes: + - by passing MIDI messages with the method processNextMidiEvent; + - by directly calling the methods noteOn, noteOff etc. + + The class implements the channel and note management logic specified in + MPE. If you pass it a message, it will know what notes on what + channels (if any) should be affected by that message. + + The class has a Listener class with the three callbacks MPENoteAdded, + MPENoteChanged, and MPENoteFinished. Implement such a + Listener class to react to note changes and trigger some functionality for + your application that depends on the MPE note state. + For example, you can use this class to write an MPE visualiser. + + If you want to write a real-time audio synth with MPE functionality, + you should instead use the classes MPESynthesiserBase, which adds + the ability to render audio and to manage voices. + + @see MPENote, MPEZoneLayout, MPESynthesiser +*/ +class JUCE_API MPEInstrument +{ +public: + + /** Constructor. + This will construct an MPE instrument with initially no MPE zones. + + In order to process incoming MIDI, call setZoneLayout, define the layout + via MIDI RPN messages, or set the instrument to legacy mode. + */ + MPEInstrument() noexcept; + + /** Destructor. */ + virtual ~MPEInstrument(); + + //========================================================================== + /** Returns the current zone layout of the instrument. + This happens by value, to enforce thread-safety and class invariants. + + Note: If the instrument is in legacy mode, the return value of this + method is unspecified. + */ + MPEZoneLayout getZoneLayout() const noexcept; + + /** Re-sets the zone layout of the instrument to the one passed in. + As a side effect, this will discard all currently playing notes, + and call noteReleased for all of them. + + This will also disable legacy mode in case it was enabled previously. + */ + void setZoneLayout (MPEZoneLayout newLayout); + + /** Returns true if the given MIDI channel (1-16) is a note channel in any + of the MPEInstrument's MPE zones; false otherwise. + When in legacy mode, this will return true if the given channel is + contained in the current legacy mode channel range; false otherwise. + */ + bool isNoteChannel (int midiChannel) const noexcept; + + /** Returns true if the given MIDI channel (1-16) is a master channel in any + of the MPEInstrument's MPE zones; false otherwise. + When in legacy mode, this will always return false. + */ + bool isMasterChannel (int midiChannel) const noexcept; + + //========================================================================== + /** The MPE note tracking mode. In case there is more than one note playing + simultaneously on the same MIDI channel, this determines which of these + notes will be modulated by an incoming MPE message on that channel + (pressure, pitchbend, or timbre). + + The default is lastNotePlayedOnChannel. + */ + enum TrackingMode + { + lastNotePlayedOnChannel, //! The most recent note on the channel that is still played (key down and/or sustained) + lowestNoteOnChannel, //! The lowest note (by initialNote) on the channel with the note key still down + highestNoteOnChannel, //! The highest note (by initialNote) on the channel with the note key still down + allNotesOnChannel //! All notes on the channel (key down and/or sustained) + }; + + /** Set the MPE tracking mode for the pressure dimension. */ + void setPressureTrackingMode (TrackingMode modeToUse); + + /** Set the MPE tracking mode for the pitchbend dimension. */ + void setPitchbendTrackingMode (TrackingMode modeToUse); + + /** Set the MPE tracking mode for the timbre dimension. */ + void setTimbreTrackingMode (TrackingMode modeToUse); + + //========================================================================== + /** Process a MIDI message and trigger the appropriate method calls + (noteOn, noteOff etc.) + + You can override this method if you need some special MIDI message + treatment on top of the standard MPE logic implemented here. + */ + virtual void processNextMidiEvent (const MidiMessage& message); + + //========================================================================== + /** Request a note-on on the given channel, with the given initial note + number and velocity. + If the message arrives on a valid note channel, this will create a + new MPENote and call the noteAdded callback. + */ + virtual void noteOn (int midiChannel, int midiNoteNumber, MPEValue midiNoteOnVelocity); + + /** Request a note-off. If there is a matching playing note, this will + release the note (except if it is sustained by a sustain or sostenuto + pedal) and call the noteReleased callback. + */ + virtual void noteOff (int midiChannel, int midiNoteNumber, MPEValue midiNoteOffVelocity); + + /** Request a pitchbend on the given channel with the given value (in units + of MIDI pitchwheel position). + Internally, this will determine whether the pitchwheel move is a + per-note pitchbend or a master pitchbend (depending on midiChannel), + take the correct per-note or master pitchbend range of the affected MPE + zone, and apply the resulting pitchbend to the affected note(s) (if any). + */ + virtual void pitchbend (int midiChannel, MPEValue pitchbend); + + /** Request a pressure change on the given channel with the given value. + This will modify the pressure dimension of the note currently held down + on this channel (if any). If the channel is a zone master channel, + the pressure change will be broadcast to all notes in this zone. + */ + virtual void pressure (int midiChannel, MPEValue value); + + /** Request a third dimension (timbre) change on the given channel with the + given value. + This will modify the timbre dimension of the note currently held down + on this channel (if any). If the channel is a zone master channel, + the timbre change will be broadcast to all notes in this zone. + */ + virtual void timbre (int midiChannel, MPEValue value); + + /** Request a sustain pedal press or release. If midiChannel is a zone's + master channel, this will act on all notes in that zone; otherwise, + nothing will happen. + */ + virtual void sustainPedal (int midiChannel, bool isDown); + + /** Request a sostenuto pedal press or release. If midiChannel is a zone's + master channel, this will act on all notes in that zone; otherwise, + nothing will happen. + */ + virtual void sostenutoPedal (int midiChannel, bool isDown); + + /** Discard all currently playing notes. + This will also call the noteReleased listener callback for all of them. + */ + void releaseAllNotes(); + + //========================================================================== + /** Returns the number of MPE notes currently played by the + instrument. + */ + int getNumPlayingNotes() const noexcept; + + /** Returns the note at the given index. If there is no such note, returns + an invalid MPENote. The notes are sorted such that the most recently + added note is the last element. + */ + MPENote getNote (int index) const noexcept; + + /** Returns the note currently playing on the given midiChannel with the + specified initial MIDI note number, if there is such a note. + Otherwise, this returns an invalid MPENote + (check with note.isValid() before use!) + */ + MPENote getNote (int midiChannel, int midiNoteNumber) const noexcept; + + /** Returns the most recent note that is playing on the given midiChannel + (this will be the note which has received the most recent note-on without + a corresponding note-off), if there is such a note. + Otherwise, this returns an invalid MPENote + (check with note.isValid() before use!) + */ + MPENote getMostRecentNote (int midiChannel) const noexcept; + + /** Returns the most recent note that is not the note passed in. + If there is no such note, this returns an invalid MPENote + (check with note.isValid() before use!) + This helper method might be useful for some custom voice handling algorithms. + */ + MPENote getMostRecentNoteOtherThan (MPENote otherThanThisNote) const noexcept; + + //========================================================================== + /** Derive from this class to be informed about any changes in the expressive + MIDI notes played by this instrument. + + Note: This listener type receives its callbacks immediately, and not + via the message thread (so you might be for example in the MIDI thread). + Therefore you should never do heavy work such as graphics rendering etc. + inside those callbacks. + */ + class Listener + { + public: + /** Constructor. */ + Listener(); + + /** Destructor. */ + virtual ~Listener(); + + /** Implement this callback to be informed whenever a new expressive + MIDI note is triggered. + */ + virtual void noteAdded (MPENote newNote) = 0; + + /** Implement this callback to be informed whenever a currently + playing MPE note's pressure value changes. + */ + virtual void notePressureChanged (MPENote changedNote) = 0; + + /** Implement this callback to be informed whenever a currently + playing MPE note's pitchbend value changes. + Note: This can happen if the note itself is bent, if there is a + master channel pitchbend event, or if both occur simultaneously. + Call MPENote::getFrequencyInHertz to get the effective note frequency. + */ + virtual void notePitchbendChanged (MPENote changedNote) = 0; + + /** Implement this callback to be informed whenever a currently + playing MPE note's timbre value changes. + */ + virtual void noteTimbreChanged (MPENote changedNote) = 0; + + /** Implement this callback to be informed whether a currently playing + MPE note's key state (whether the key is down and/or the note is + sustained) has changed. + Note: if the key state changes to MPENote::off, noteReleased is + called instead. + */ + virtual void noteKeyStateChanged (MPENote changedNote) = 0; + + /** Implement this callback to be informed whenever an MPE note + is released (either by a note-off message, or by a sustain/sostenuto + pedal release for a note that already received a note-off), + and should therefore stop playing. + */ + virtual void noteReleased (MPENote finishedNote) = 0; + }; + + //========================================================================== + /** Adds a listener. */ + void addListener (Listener* const listenerToAdd) noexcept; + + /** Removes a listener. */ + void removeListener (Listener* const listenerToRemove) noexcept; + + //========================================================================== + /** Puts the instrument into legacy mode. + As a side effect, this will discard all currently playing notes, + and call noteReleased for all of them. + + This special zone layout mode is for backwards compatibility with + non-MPE MIDI devices. In this mode, the instrument will ignore the + current MPE zone layout. It will instead take a range of MIDI channels + (default: all channels 1-16) and treat them as note channels, with no + master channel. MIDI channels outside of this range will be ignored. + + @param pitchbendRange The note pitchbend range in semitones to use when in legacy mode. + Must be between 0 and 96, otherwise behaviour is undefined. + The default pitchbend range in legacy mode is +/- 2 semitones. + + @param channelRange The range of MIDI channels to use for notes when in legacy mode. + The default is to use all MIDI channels (1-16). + + To get out of legacy mode, set a new MPE zone layout using setZoneLayout. + */ + void enableLegacyMode (int pitchbendRange = 2, + Range channelRange = Range (1, 17)); + + /** Returns true if the instrument is in legacy mode, false otherwise. */ + bool isLegacyModeEnabled() const noexcept; + + /** Returns the range of MIDI channels (1-16) to be used for notes when in legacy mode. */ + Range getLegacyModeChannelRange() const noexcept; + + /** Re-sets the range of MIDI channels (1-16) to be used for notes when in legacy mode. */ + void setLegacyModeChannelRange (Range channelRange); + + /** Returns the pitchbend range in semitones (0-96) to be used for notes when in legacy mode. */ + int getLegacyModePitchbendRange() const noexcept; + + /** Re-sets the pitchbend range in semitones (0-96) to be used for notes when in legacy mode. */ + void setLegacyModePitchbendRange (int pitchbendRange); + +protected: + //========================================================================== + /** This method defines what initial pitchbend value should be used for newly + triggered notes. The default is to use the last pitchbend value + that has been received on the same MIDI channel (or no pitchbend + if no pitchbend messages have been received so far). + Override this method if you need different behaviour. + */ + virtual MPEValue getInitialPitchbendForNoteOn (int midiChannel, + int midiNoteNumber, + MPEValue midiNoteOnVelocity) const; + + /** This method defines what initial pressure value should be used for newly + triggered notes. The default is to re-use the note-on velocity value. + Override this method if you need different behaviour. + */ + virtual MPEValue getInitialPressureForNoteOn (int midiChannel, + int midiNoteNumber, + MPEValue midiNoteOnVelocity) const; + + /** This method defines what initial timbre value should be used for newly + triggered notes. The default is to use the last timbre value that has + that has been received on the same MIDI channel (or a neutral centred value + if no pitchbend messages have been received so far). + Override this method if you need different behaviour. + */ + virtual MPEValue getInitialTimbreForNoteOn (int midiChannel, + int midiNoteNumber, + MPEValue midiNoteOnVelocity) const; + +private: + //========================================================================== + CriticalSection lock; + Array notes; + MPEZoneLayout zoneLayout; + ListenerList listeners; + + uint8 lastPressureLowerBitReceivedOnChannel[16]; + uint8 lastTimbreLowerBitReceivedOnChannel[16]; + bool isNoteChannelSustained[16]; + + struct LegacyMode + { + bool isEnabled; + Range channelRange; + int pitchbendRange; + }; + + struct MPEDimension + { + MPEDimension() noexcept : trackingMode (lastNotePlayedOnChannel) {} + TrackingMode trackingMode; + MPEValue lastValueReceivedOnChannel[16]; + MPEValue MPENote::* value; + MPEValue& getValue (MPENote& note) noexcept { return note.*(value); } + }; + + LegacyMode legacyMode; + MPEDimension pitchbendDimension, pressureDimension, timbreDimension; + + void updateDimension (int midiChannel, MPEDimension&, MPEValue); + void updateDimensionMaster (MPEZone&, MPEDimension&, MPEValue); + void updateDimensionForNote (MPENote&, MPEDimension&, MPEValue); + void callListenersDimensionChanged (MPENote&, MPEDimension&); + + void processMidiNoteOnMessage (const MidiMessage&); + void processMidiNoteOffMessage (const MidiMessage&); + void processMidiPitchWheelMessage (const MidiMessage&); + void processMidiChannelPressureMessage (const MidiMessage&); + void processMidiControllerMessage (const MidiMessage&); + void processMidiAllNotesOffMessage (const MidiMessage&); + void handlePressureMSB (int midiChannel, int value) noexcept; + void handlePressureLSB (int midiChannel, int value) noexcept; + void handleTimbreMSB (int midiChannel, int value) noexcept; + void handleTimbreLSB (int midiChannel, int value) noexcept; + void handleSustainOrSostenuto (int midiChannel, bool isDown, bool isSostenuto); + + MPENote* getNotePtr (int midiChannel, int midiNoteNumber) const noexcept; + MPENote* getNotePtr (int midiChannel, TrackingMode) const noexcept; + MPENote* getLastNotePlayedPtr (int midiChannel) const noexcept; + MPENote* getHighestNotePtr (int midiChannel) const noexcept; + MPENote* getLowestNotePtr (int midiChannel) const noexcept; + void updateNoteTotalPitchbend (MPENote&); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPEInstrument) +}; + + +#endif // JUCE_MPE_H_INCLUDED diff --git a/source/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp b/source/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp new file mode 100644 index 000000000..5102e5505 --- /dev/null +++ b/source/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp @@ -0,0 +1,198 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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. + + ============================================================================== +*/ + + +MidiBuffer MPEMessages::addZone (MPEZone zone) +{ + MidiBuffer buffer (MidiRPNGenerator::generate (zone.getFirstNoteChannel(), + zoneLayoutMessagesRpnNumber, + zone.getNumNoteChannels(), + false, false)); + + buffer.addEvents (perNotePitchbendRange (zone), 0, -1, 0); + buffer.addEvents (masterPitchbendRange (zone), 0, -1, 0); + + return buffer; +} + +MidiBuffer MPEMessages::perNotePitchbendRange (MPEZone zone) +{ + return MidiRPNGenerator::generate (zone.getFirstNoteChannel(), 0, + zone.getPerNotePitchbendRange(), + false, false); +} + +MidiBuffer MPEMessages::masterPitchbendRange (MPEZone zone) +{ + return MidiRPNGenerator::generate (zone.getMasterChannel(), 0, + zone.getMasterPitchbendRange(), + false, false); +} + +MidiBuffer MPEMessages::clearAllZones() +{ + return MidiRPNGenerator::generate (1, zoneLayoutMessagesRpnNumber, 16, false, false); +} + +MidiBuffer MPEMessages::setZoneLayout (const MPEZoneLayout& layout) +{ + MidiBuffer buffer; + + buffer.addEvents (clearAllZones(), 0, -1, 0); + + for (int i = 0; i < layout.getNumZones(); ++i) + buffer.addEvents (addZone (*layout.getZoneByIndex (i)), 0, -1, 0); + + return buffer; +} + +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +class MPEMessagesTests : public UnitTest +{ +public: + MPEMessagesTests() : UnitTest ("MPEMessages class") {} + + void runTest() override + { + beginTest ("add zone"); + { + { + MidiBuffer buffer = MPEMessages::addZone (MPEZone (1, 7)); + + const uint8 expectedBytes[] = + { + 0xb1, 0x64, 0x06, 0xb1, 0x65, 0x00, 0xb1, 0x06, 0x07, // set up zone + 0xb1, 0x64, 0x00, 0xb1, 0x65, 0x00, 0xb1, 0x06, 0x30, // per-note pbrange (default = 48) + 0xb0, 0x64, 0x00, 0xb0, 0x65, 0x00, 0xb0, 0x06, 0x02 // master pbrange (default = 2) + }; + + testMidiBuffer (buffer, expectedBytes, sizeof (expectedBytes)); + } + { + MidiBuffer buffer = MPEMessages::addZone (MPEZone (11, 5, 96, 0)); + + const uint8 expectedBytes[] = + { + 0xbb, 0x64, 0x06, 0xbb, 0x65, 0x00, 0xbb, 0x06, 0x05, // set up zone + 0xbb, 0x64, 0x00, 0xbb, 0x65, 0x00, 0xbb, 0x06, 0x60, // per-note pbrange (custom) + 0xba, 0x64, 0x00, 0xba, 0x65, 0x00, 0xba, 0x06, 0x00 // master pbrange (custom) + }; + + testMidiBuffer (buffer, expectedBytes, sizeof (expectedBytes)); + } + } + + beginTest ("set per-note pitchbend range"); + { + MPEZone zone (3, 7, 96); + MidiBuffer buffer = MPEMessages::perNotePitchbendRange (zone); + + const uint8 expectedBytes[] = { 0xb3, 0x64, 0x00, 0xb3, 0x65, 0x00, 0xb3, 0x06, 0x60 }; + + testMidiBuffer (buffer, expectedBytes, sizeof (expectedBytes)); + } + + + beginTest ("set master pitchbend range"); + { + MPEZone zone (3, 7, 48, 60); + MidiBuffer buffer = MPEMessages::masterPitchbendRange (zone); + + const uint8 expectedBytes[] = { 0xb2, 0x64, 0x00, 0xb2, 0x65, 0x00, 0xb2, 0x06, 0x3c }; + + testMidiBuffer (buffer, expectedBytes, sizeof (expectedBytes)); + } + + beginTest ("clear all zones"); + { + MidiBuffer buffer = MPEMessages::clearAllZones(); + + const uint8 expectedBytes[] = { 0xb0, 0x64, 0x06, 0xb0, 0x65, 0x00, 0xb0, 0x06, 0x10 }; + + testMidiBuffer (buffer, expectedBytes, sizeof (expectedBytes)); + } + + beginTest ("set complete state"); + { + MPEZoneLayout layout; + layout.addZone (MPEZone (1, 7, 96, 0)); + layout.addZone (MPEZone (9, 7)); + layout.addZone (MPEZone (5, 3)); + layout.addZone (MPEZone (5, 4)); + layout.addZone (MPEZone (6, 4)); + + MidiBuffer buffer = MPEMessages::setZoneLayout (layout); + + const uint8 expectedBytes[] = { + 0xb0, 0x64, 0x06, 0xb0, 0x65, 0x00, 0xb0, 0x06, 0x10, // clear all zones + 0xb1, 0x64, 0x06, 0xb1, 0x65, 0x00, 0xb1, 0x06, 0x03, // set zone 1 (1, 3) + 0xb1, 0x64, 0x00, 0xb1, 0x65, 0x00, 0xb1, 0x06, 0x60, // per-note pbrange (custom) + 0xb0, 0x64, 0x00, 0xb0, 0x65, 0x00, 0xb0, 0x06, 0x00, // master pbrange (custom) + 0xb6, 0x64, 0x06, 0xb6, 0x65, 0x00, 0xb6, 0x06, 0x04, // set zone 2 (6, 4) + 0xb6, 0x64, 0x00, 0xb6, 0x65, 0x00, 0xb6, 0x06, 0x30, // per-note pbrange (default = 48) + 0xb5, 0x64, 0x00, 0xb5, 0x65, 0x00, 0xb5, 0x06, 0x02 // master pbrange (default = 2) + }; + + testMidiBuffer (buffer, expectedBytes, sizeof (expectedBytes)); + } + } + +private: + //========================================================================== + void testMidiBuffer (MidiBuffer& buffer, const uint8* expectedBytes, int expectedBytesSize) + { + uint8 actualBytes[128] = { 0 }; + extractRawBinaryData (buffer, actualBytes, sizeof (actualBytes)); + + expectEquals (std::memcmp (actualBytes, expectedBytes, (std::size_t) expectedBytesSize), 0); + } + + //========================================================================== + void extractRawBinaryData (const MidiBuffer& midiBuffer, const uint8* bufferToCopyTo, std::size_t maxBytes) + { + std::size_t pos = 0; + MidiBuffer::Iterator iter (midiBuffer); + MidiMessage midiMessage; + int samplePosition; // Note: not actually used, so no need to initialise. + + while (iter.getNextEvent (midiMessage, samplePosition)) + { + const uint8* data = midiMessage.getRawData(); + std::size_t dataSize = (std::size_t) midiMessage.getRawDataSize(); + + if (pos + dataSize > maxBytes) + return; + + std::memcpy ((void*) (bufferToCopyTo + pos), data, dataSize); + pos += dataSize; + } + } +}; + +static MPEMessagesTests MPEMessagesUnitTests; + +#endif // JUCE_UNIT_TESTS diff --git a/source/modules/juce_audio_basics/mpe/juce_MPEMessages.h b/source/modules/juce_audio_basics/mpe/juce_MPEMessages.h new file mode 100644 index 000000000..f051cd867 --- /dev/null +++ b/source/modules/juce_audio_basics/mpe/juce_MPEMessages.h @@ -0,0 +1,96 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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_MPEMESSAGES_H_INCLUDED +#define JUCE_MPEMESSAGES_H_INCLUDED + + +//============================================================================== +/** + This helper class contains the necessary helper functions to generate + MIDI messages that are exclusive to MPE, such as defining + MPE zones and setting per-note and master pitchbend ranges. + You can then send them to your MPE device using + MidiOutput::sendBlockOfMessagesNow. + + All other MPE messages like per-note pitchbend, pressure, and third + dimension, are ordinary MIDI messages that should be created using the MidiMessage + class instead. You just need to take care to send them to the appropriate + per-note MIDI channel. + + Note: if you are working with an MPEZoneLayout object inside your app, + you should not use the message sequences provided here. Instead, you should + change the zone layout programmatically with the member functions provided in the + MPEZoneLayout class itself. You should also make sure that the Expressive + MIDI zone layout of your C++ code and of the MPE device are kept in sync. + + @see MidiMessage, MPEZoneLayout, MPEZone +*/ +class JUCE_API MPEMessages +{ +public: + /** Returns the sequence of MIDI messages that, if sent to an Expressive + MIDI device, will define a new MPE zone. + */ + static MidiBuffer addZone (MPEZone zone); + + /** Returns the sequence of MIDI messages that, if sent to an Expressive + MIDI device, will change the per-note pitchbend range of an + existing MPE zone. + */ + static MidiBuffer perNotePitchbendRange (MPEZone zone); + + /** Returns the sequence of MIDI messages that, if sent to an Expressive + MIDI device, will change the master pitchbend range of an + existing MPE zone. + */ + static MidiBuffer masterPitchbendRange (MPEZone zone); + + /** Returns the sequence of MIDI messages that, if sent to an Expressive + MIDI device, will erase all currently defined MPE zones. + */ + static MidiBuffer clearAllZones(); + + /** Returns the sequence of MIDI messages that, if sent to an Expressive + MIDI device, will reset the whole MPE zone layout of the + device to the laoyut passed in. This will first clear all currently + defined MPE zones, then add all zones contained in the + passed-in zone layout, and set their per-note and master pitchbend + ranges to their current values. + */ + static MidiBuffer setZoneLayout (const MPEZoneLayout& layout); + + /** The RPN number used for MPE zone layout messages. + + Note: This number can change in later versions of MPE. + + Pitchbend range messages (both per-note and master) are instead sent + on RPN 0 as in standard MIDI 1.0. + */ + static const int zoneLayoutMessagesRpnNumber = 6; +}; + + + +#endif // JUCE_MPEMESSAGES_H_INCLUDED diff --git a/source/modules/juce_audio_basics/mpe/juce_MPENote.cpp b/source/modules/juce_audio_basics/mpe/juce_MPENote.cpp new file mode 100644 index 000000000..f07fe163b --- /dev/null +++ b/source/modules/juce_audio_basics/mpe/juce_MPENote.cpp @@ -0,0 +1,132 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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 +{ + uint16 generateNoteID (int midiChannel, int midiNoteNumber) noexcept + { + jassert (midiChannel > 0 && midiChannel <= 16); + jassert (midiNoteNumber >= 0 && midiNoteNumber < 128); + + return uint16 ((midiChannel << 7) + midiNoteNumber); + } +} + +//============================================================================== +MPENote::MPENote (int midiChannel_, + int initialNote_, + MPEValue noteOnVelocity_, + MPEValue pitchbend_, + MPEValue pressure_, + MPEValue timbre_, + KeyState keyState_) noexcept + : noteID (generateNoteID (midiChannel_, initialNote_)), + midiChannel (uint8 (midiChannel_)), + initialNote (uint8 (initialNote_)), + noteOnVelocity (noteOnVelocity_), + pitchbend (pitchbend_), + pressure (pressure_), + timbre (timbre_), + noteOffVelocity (MPEValue::minValue()), + keyState (keyState_) +{ + jassert (keyState != MPENote::off); + jassert (isValid()); +} + +MPENote::MPENote() noexcept + : noteID (0), + midiChannel (0), + initialNote (0), + noteOnVelocity (MPEValue::minValue()), + pitchbend (MPEValue::centreValue()), + pressure (MPEValue::centreValue()), + timbre (MPEValue::centreValue()), + noteOffVelocity (MPEValue::minValue()), + keyState (MPENote::off) +{ +} + +//============================================================================== +bool MPENote::isValid() const noexcept +{ + return midiChannel > 0 && midiChannel <= 16 && initialNote >= 0 && initialNote <= 127; +} + +//============================================================================== +double MPENote::getFrequencyInHertz (double frequencyOfA) const noexcept +{ + double pitchInSemitones = double (initialNote) + totalPitchbendInSemitones; + return frequencyOfA * std::pow (2.0, (pitchInSemitones - 69.0) / 12.0); +} + +//============================================================================== +bool MPENote::operator== (const MPENote& other) const noexcept +{ + jassert (isValid() && other.isValid()); + return noteID == other.noteID; +} + +bool MPENote::operator!= (const MPENote& other) const noexcept +{ + jassert (isValid() && other.isValid()); + return noteID != other.noteID; +} + +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +class MPENoteTests : public UnitTest +{ +public: + MPENoteTests() : UnitTest ("MPENote class") {} + + //========================================================================== + void runTest() override + { + beginTest ("getFrequencyInHertz"); + { + MPENote note; + note.initialNote = 60; + note.totalPitchbendInSemitones = -0.5; + expectEqualsWithinOneCent (note.getFrequencyInHertz(), 254.178); + } + } + +private: + //========================================================================== + void expectEqualsWithinOneCent (double frequencyInHertzActual, + double frequencyInHertzExpected) + { + double ratio = frequencyInHertzActual / frequencyInHertzExpected; + double oneCent = 1.0005946; + expect (ratio < oneCent); + expect (ratio > 1.0 / oneCent); + } +}; + +static MPENoteTests MPENoteUnitTests; + +#endif // JUCE_UNIT_TESTS diff --git a/source/modules/juce_audio_basics/mpe/juce_MPENote.h b/source/modules/juce_audio_basics/mpe/juce_MPENote.h new file mode 100644 index 000000000..9e979a41e --- /dev/null +++ b/source/modules/juce_audio_basics/mpe/juce_MPENote.h @@ -0,0 +1,180 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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_MPENOTE_H_INCLUDED +#define JUCE_MPENOTE_H_INCLUDED + + +//============================================================================== +/** + This struct represents a playing MPE note. + + A note is identified by a unique ID, or alternatively, by a MIDI channel + and an initial note. It is characterised by five dimensions of continuous + expressive control. Their current values are represented as + MPEValue objects. + + @see MPEValue +*/ +struct JUCE_API MPENote +{ + //========================================================================== + enum KeyState + { + off = 0, + keyDown = 1, + sustained = 2, + keyDownAndSustained = 3 + }; + + //========================================================================== + /** Constructor. + + @param midiChannel The MIDI channel of the note, between 2 and 16. + (Channel 1 can never be a note channel in MPE). + + @param initialNote The MIDI note number, between 0 and 127. + + @param velocity The note-on velocity of the note. + + @param pitchbend The initial per-note pitchbend of the note. + + @param pressure The initial pressure of the note. + + @param timbre The timbre value of the note. + + @param keyState The key state of the note (whether the key is down + and/or the note is sustained). This value must not + be MPENote::off, since you are triggering a new note. + (If not specified, the default value will be MPENOte::keyDown.) + */ + MPENote (int midiChannel, + int initialNote, + MPEValue velocity, + MPEValue pitchbend, + MPEValue pressure, + MPEValue timbre, + KeyState keyState = MPENote::keyDown) noexcept; + + /** Default constructor. + + Constructs an invalid MPE note (a note with the key state MPENote::off + and an invalid MIDI channel. The only allowed use for such a note is to + call isValid() on it; everything else is undefined behaviour. + */ + MPENote() noexcept; + + /** Checks whether the MPE note is valid. */ + bool isValid() const noexcept; + + //========================================================================== + // Invariants that define the note. + + /** A unique ID. Useful to distinguish the note from other simultaneously + sounding notes that may use the same note number or MIDI channel. + This should never change during the lifetime of a note object. + */ + uint16 noteID; + + /** The MIDI channel which this note uses. + This should never change during the lifetime of an MPENote object. + */ + uint8 midiChannel; + + /** The MIDI note number that was sent when the note was triggered. + This should never change during the lifetime of an MPENote object. + */ + uint8 initialNote; + + //========================================================================== + // The five dimensions of continuous expressive control + + /** The velocity ("strike") of the note-on. + This dimension will stay constant after the note has been turned on. + */ + MPEValue noteOnVelocity; + + /** Current per-note pitchbend of the note (in units of MIDI pitchwheel + position). This dimension can be modulated while the note sounds. + + Note: This value is not aware of the currently used pitchbend range, + or an additional master pitchbend that may be simultaneously applied. + To compute the actual effective pitchbend of an MPENote, you should + probably use the member totalPitchbendInSemitones instead. + + @see totalPitchbendInSemitones, getFrequencyInHertz + */ + MPEValue pitchbend; + + /** Current pressure with which the note is held down. + This dimension can be modulated while the note sounds. + */ + MPEValue pressure; + + /** Current value of the note's third expressive dimension, tyically + encoding some kind of timbre parameter. + This dimension can be modulated while the note sounds. + */ + MPEValue timbre; + + /** The release velocity ("lift") of the note after a note-off has been + received. + This dimension will only have a meaningful value after a note-off has + been received for the note (and keyState is set to MPENote::off or + MPENOte::sustained). Initially, the value is undefined. + */ + MPEValue noteOffVelocity; + + //========================================================================== + /** Current effective pitchbend of the note in units of semitones, relative + to initialNote. You should use this to compute the actual effective pitch + of the note. This value is computed and set by an MPEInstrument to the + sum of the per-note pitchbend value (stored in MPEValue::pitchbend) + and the master pitchbend of the MPE zone, weighted with the per-note + pitchbend range and master pitchbend range of the zone, respectively. + + @see getFrequencyInHertz + */ + double totalPitchbendInSemitones; + + /** Current key state. Indicates whether the note key is currently down (pressed) + and/or the note is sustained (by a sustain or sostenuto pedal). + */ + KeyState keyState; + + //========================================================================== + /** Returns the current frequency of the note in Hertz. This is the a sum of + the initialNote and the totalPitchbendInSemitones, converted to Hertz. + */ + double getFrequencyInHertz (double frequencyOfA = 440.0) const noexcept; + + /** Returns true if two notes are the same, determined by their unique ID. */ + bool operator== (const MPENote& other) const noexcept; + + /** Returns true if two notes are different notes, determined by their unique ID. */ + bool operator!= (const MPENote& other) const noexcept; +}; + + +#endif // JUCE_MPENOTE_H_INCLUDED diff --git a/source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp b/source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp new file mode 100644 index 000000000..58c85c3b6 --- /dev/null +++ b/source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp @@ -0,0 +1,356 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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. + + ============================================================================== +*/ + +MPESynthesiser::MPESynthesiser() +{ +} + +MPESynthesiser::MPESynthesiser (MPEInstrument* instrument) : MPESynthesiserBase (instrument) +{ +} + +MPESynthesiser::~MPESynthesiser() +{ +} + +//============================================================================== +void MPESynthesiser::startVoice (MPESynthesiserVoice* voice, MPENote noteToStart) +{ + jassert (voice != nullptr); + voice->currentlyPlayingNote = noteToStart; + voice->noteStarted(); +} + +void MPESynthesiser::stopVoice (MPESynthesiserVoice* voice, MPENote noteToStop, bool allowTailOff) +{ + jassert (voice != nullptr); + voice->currentlyPlayingNote = noteToStop; + voice->noteStopped (allowTailOff); +} + +//============================================================================== +void MPESynthesiser::noteAdded (MPENote newNote) +{ + const ScopedLock sl (voicesLock); + + if (MPESynthesiserVoice* voice = findFreeVoice (newNote, shouldStealVoices)) + startVoice (voice, newNote); +} + +void MPESynthesiser::notePressureChanged (MPENote changedNote) +{ + const ScopedLock sl (voicesLock); + + for (int i = 0; i < voices.size(); ++i) + { + MPESynthesiserVoice* voice = voices.getUnchecked (i); + + if (voice->isCurrentlyPlayingNote (changedNote)) + { + voice->currentlyPlayingNote = changedNote; + voice->notePressureChanged(); + } + } +} + +void MPESynthesiser::notePitchbendChanged (MPENote changedNote) +{ + const ScopedLock sl (voicesLock); + + for (int i = 0; i < voices.size(); ++i) + { + MPESynthesiserVoice* voice = voices.getUnchecked (i); + + if (voice->isCurrentlyPlayingNote (changedNote)) + { + voice->currentlyPlayingNote = changedNote; + voice->notePitchbendChanged(); + } + } +} + +void MPESynthesiser::noteTimbreChanged (MPENote changedNote) +{ + const ScopedLock sl (voicesLock); + + for (int i = 0; i < voices.size(); ++i) + { + MPESynthesiserVoice* voice = voices.getUnchecked (i); + + if (voice->isCurrentlyPlayingNote (changedNote)) + { + voice->currentlyPlayingNote = changedNote; + voice->noteTimbreChanged(); + } + } +} + +void MPESynthesiser::noteKeyStateChanged (MPENote changedNote) +{ + const ScopedLock sl (voicesLock); + + for (int i = 0; i < voices.size(); ++i) + { + MPESynthesiserVoice* voice = voices.getUnchecked (i); + + if (voice->isCurrentlyPlayingNote (changedNote)) + { + voice->currentlyPlayingNote = changedNote; + voice->noteKeyStateChanged(); + } + } +} + +void MPESynthesiser::noteReleased (MPENote finishedNote) +{ + const ScopedLock sl (voicesLock); + + for (int i = voices.size(); --i >= 0;) + { + MPESynthesiserVoice* const voice = voices.getUnchecked (i); + + if (voice->isCurrentlyPlayingNote(finishedNote)) + stopVoice (voice, finishedNote, true); + } +} + +void MPESynthesiser::setCurrentPlaybackSampleRate (const double newRate) +{ + MPESynthesiserBase::setCurrentPlaybackSampleRate (newRate); + + const ScopedLock sl (voicesLock); + + turnOffAllVoices (false); + + for (int i = voices.size(); --i >= 0;) + voices.getUnchecked (i)->setCurrentSampleRate (newRate); +} + +void MPESynthesiser::handleMidiEvent (const MidiMessage& m) +{ + if (m.isController()) + handleController (m.getChannel(), m.getControllerNumber(), m.getControllerValue()); + else if (m.isProgramChange()) + handleProgramChange (m.getChannel(), m.getProgramChangeNumber()); + + MPESynthesiserBase::handleMidiEvent (m); +} + +MPESynthesiserVoice* MPESynthesiser::findFreeVoice (MPENote noteToFindVoiceFor, bool stealIfNoneAvailable) const +{ + const ScopedLock sl (voicesLock); + + for (int i = 0; i < voices.size(); ++i) + { + MPESynthesiserVoice* const voice = voices.getUnchecked (i); + + if (! voice->isActive()) + return voice; + } + + if (stealIfNoneAvailable) + return findVoiceToSteal (noteToFindVoiceFor); + + return nullptr; +} + +struct MPEVoiceAgeSorter +{ + static int compareElements (MPESynthesiserVoice* v1, MPESynthesiserVoice* v2) noexcept + { + return v1->wasStartedBefore (*v2) ? -1 : (v2->wasStartedBefore (*v1) ? 1 : 0); + } +}; + +MPESynthesiserVoice* MPESynthesiser::findVoiceToSteal (MPENote noteToStealVoiceFor) const +{ + // This voice-stealing algorithm applies the following heuristics: + // - Re-use the oldest notes first + // - Protect the lowest & topmost notes, even if sustained, but not if they've been released. + + + // apparently you are trying to render audio without having any voices... + jassert (voices.size() > 0); + + // These are the voices we want to protect (ie: only steal if unavoidable) + MPESynthesiserVoice* low = nullptr; // Lowest sounding note, might be sustained, but NOT in release phase + MPESynthesiserVoice* top = nullptr; // Highest sounding note, might be sustained, but NOT in release phase + + // 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) + { + MPESynthesiserVoice* const voice = voices.getUnchecked (i); + jassert (voice->isActive()); // We wouldn't be here otherwise + + MPEVoiceAgeSorter sorter; + usableVoices.addSorted (sorter, voice); + + if (! voice->isPlayingButReleased()) // Don't protect released notes + { + const int noteNumber = voice->getCurrentlyPlayingNote().initialNote; + + if (low == nullptr || noteNumber < low->getCurrentlyPlayingNote().initialNote) + low = voice; + + if (top == nullptr || noteNumber > top->getCurrentlyPlayingNote().initialNote) + top = voice; + } + } + + // Eliminate pathological cases (ie: only 1 note playing): we always give precedence to the lowest note(s) + if (top == low) + top = nullptr; + + const int numUsableVoices = usableVoices.size(); + + // If we want to re-use the voice to trigger a new note, + // then The oldest note that's playing the same note number is ideal. + if (noteToStealVoiceFor.isValid()) + { + for (int i = 0; i < numUsableVoices; ++i) + { + MPESynthesiserVoice* const voice = usableVoices.getUnchecked (i); + + if (voice->getCurrentlyPlayingNote().initialNote == noteToStealVoiceFor.initialNote) + return voice; + } + } + + // Oldest voice that has been released (no finger on it and not held by sustain pedal) + for (int i = 0; i < numUsableVoices; ++i) + { + MPESynthesiserVoice* const voice = usableVoices.getUnchecked (i); + + if (voice != low && voice != top && voice->isPlayingButReleased()) + return voice; + } + + // Oldest voice that doesn't have a finger on it: + for (int i = 0; i < numUsableVoices; ++i) + { + MPESynthesiserVoice* const voice = usableVoices.getUnchecked (i); + + if (voice != low && voice != top + && voice->getCurrentlyPlayingNote().keyState != MPENote::keyDown + && voice->getCurrentlyPlayingNote().keyState != MPENote::keyDownAndSustained) + return voice; + } + + // Oldest voice that isn't protected + for (int i = 0; i < numUsableVoices; ++i) + { + MPESynthesiserVoice* const voice = usableVoices.getUnchecked (i); + + if (voice != low && voice != top) + return voice; + } + + // We've only got "protected" voices now: lowest note takes priority + jassert (low != nullptr); + + // Duophonic synth: give priority to the bass note: + if (top != nullptr) + return top; + + return low; +} + +//============================================================================== +void MPESynthesiser::addVoice (MPESynthesiserVoice* const newVoice) +{ + const ScopedLock sl (voicesLock); + newVoice->setCurrentSampleRate (getSampleRate()); + voices.add (newVoice); +} + +void MPESynthesiser::clearVoices() +{ + const ScopedLock sl (voicesLock); + voices.clear(); +} + +MPESynthesiserVoice* MPESynthesiser::getVoice (const int index) const +{ + const ScopedLock sl (voicesLock); + return voices [index]; +} + +void MPESynthesiser::removeVoice (const int index) +{ + const ScopedLock sl (voicesLock); + voices.remove (index); +} + +void MPESynthesiser::reduceNumVoices (const int newNumVoices) +{ + // we can't possibly get to a negative number of voices... + jassert (newNumVoices >= 0); + + const ScopedLock sl (voicesLock); + + while (voices.size() > newNumVoices) + { + if (MPESynthesiserVoice* voice = findFreeVoice (MPENote(), true)) + voices.removeObject (voice); + else + voices.remove (0); // if there's no voice to steal, kill the oldest voice + } +} + +void MPESynthesiser::turnOffAllVoices (bool allowTailOff) +{ + // first turn off all voices (it's more efficient to do this immediately + // rather than to go through the MPEInstrument for this). + for (int i = voices.size(); --i >= 0;) + voices.getUnchecked (i)->noteStopped (allowTailOff); + + // finally make sure the MPE Instrument also doesn't have any notes anymore. + instrument->releaseAllNotes(); +} + +//============================================================================== +void MPESynthesiser::renderNextSubBlock (AudioBuffer& buffer, int startSample, int numSamples) +{ + for (int i = voices.size(); --i >= 0;) + { + MPESynthesiserVoice* voice = voices.getUnchecked (i); + + if (voice->isActive()) + voice->renderNextBlock (buffer, startSample, numSamples); + } +} + +void MPESynthesiser::renderNextSubBlock (AudioBuffer& buffer, int startSample, int numSamples) +{ + for (int i = voices.size(); --i >= 0;) + { + MPESynthesiserVoice* voice = voices.getUnchecked (i); + + if (voice->isActive()) + voice->renderNextBlock (buffer, startSample, numSamples); + } +} diff --git a/source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h b/source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h new file mode 100644 index 000000000..cbca499e8 --- /dev/null +++ b/source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h @@ -0,0 +1,313 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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_MPESynthesiser_H_INCLUDED +#define JUCE_MPESynthesiser_H_INCLUDED + + +//============================================================================== +/** + Base class for an MPE-compatible musical device that can play sounds. + + This class extends MPESynthesiserBase by adding the concept of voices, + each of which can play a sound triggered by a MPENote that can be modulated + by MPE dimensions like pressure, pitchbend, and timbre, while the note is + sounding. + + To create a synthesiser, you'll need to create a subclass of MPESynthesiserVoice + which can play back one of these sounds at a time. + + Then you can use the addVoice() methods to give the synthesiser a set of voices + it can use to play notes. 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 (inherited + from MPESynthesiserBase). The voices will be started, stopped, and modulated + automatically, based on the MPE/MIDI messages that the synthesiser receives. + + 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. + + @see MPESynthesiserBase, MPESythesiserVoice, MPENote, MPEInstrument +*/ +class JUCE_API MPESynthesiser : public MPESynthesiserBase +{ +public: + //========================================================================== + /** Constructor. + You'll need to add some voices before it'll make any sound. + + @see addVoice + */ + MPESynthesiser(); + + /** Constructor to pass to the synthesiser a custom MPEInstrument object + to handle the MPE note state, MIDI channel assignment etc. + (in case you need custom logic for this that goes beyond MIDI and MPE). + The synthesiser will take ownership of this object. + + @see MPESynthesiserBase, MPEInstrument + */ + MPESynthesiser (MPEInstrument* instrument); + + /** Destructor. */ + ~MPESynthesiser(); + + //========================================================================== + /** 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. */ + MPESynthesiserVoice* 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. + */ + void addVoice (MPESynthesiserVoice* newVoice); + + /** Deletes one of the voices. */ + void removeVoice (int index); + + /** Reduces the number of voices to newNumVoices. + + This will repeatedly call findVoiceToSteal() and remove that voice, until + the total number of voices equals newNumVoices. If newNumVoices is greater than + or equal to the current number of voices, this method does nothing. + */ + void reduceNumVoices (int newNumVoices); + + /** Release all MPE notes and turn off all voices. + + 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 is meant to be called by the user, for example to implement + a MIDI panic button in a synth. + */ + virtual void turnOffAllVoices (bool allowTailOff); + + //========================================================================== + /** 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 setVoiceStealingEnabled (bool shouldSteal) noexcept { shouldStealVoices = shouldSteal; } + + /** Returns true if note-stealing is enabled. */ + bool isVoiceStealingEnabled() const noexcept { return shouldStealVoices; } + + //========================================================================== + /** Tells the synthesiser what the sample rate is for the audio it's being used to render. + + This overrides the implementation in MPESynthesiserBase, to additionally + propagate the new value to the voices so that they can use it to render the correct + pitches. + */ + void setCurrentPlaybackSampleRate (double newRate) override; + + //========================================================================== + /** Handle incoming MIDI events. + + This method will be called automatically according to the MIDI data passed + into renderNextBlock(), but you can also call it yourself to manually + inject MIDI events. + + This implementation forwards program change messages and non-MPE-related + controller messages to handleProgramChange and handleController, respectively, + and then simply calls through to MPESynthesiserBase::handleMidiEvent to deal + with MPE-related MIDI messages used for MPE notes, zones etc. + + This method can be overridden further if you need to do custom MIDI + handling on top of what is provided here. + */ + void handleMidiEvent (const MidiMessage&) override; + + /** Callback for MIDI controller messages. The default implementation + provided here does nothing; override this method if you need custom + MIDI controller handling on top of MPE. + + This method will be called automatically according to the midi data passed into + renderNextBlock(). + */ + virtual void handleController (int /*midiChannel*/, + int /*controllerNumber*/, + int /*controllerValue*/) {} + + /** Callback for MIDI program change messages. The default implementation + provided here does nothing; override this method if you need to handle + those messages. + + This method will be called automatically according to the midi data passed into + renderNextBlock(). + */ + virtual void handleProgramChange (int /*midiChannel*/, + int /*programNumber*/) {} + +protected: + //============================================================================== + /** Attempts to start playing a new note. + + The default method here will find a free voice that is appropriate for + playing the given MPENote, and use that voice to start playing the sound. + If isNoteStealingEnabled returns true (set this by calling setNoteStealingEnabled), + the synthesiser will use the voice stealing algorithm to find a free voice for + the note (if no voices are free otherwise). + + This method will be called automatically according to the midi data passed into + renderNextBlock(). Do not call it yourself, otherwise the internal MPE note state + will become inconsistent. + */ + virtual void noteAdded (MPENote newNote) override; + + /** Stops playing a note. + + This will be called whenever an MPE note is released (either by a note-off message, + or by a sustain/sostenuto pedal release for a note that already received a note-off), + and should therefore stop playing. + + This will find any voice that is currently playing finishedNote, + turn its currently playing note off, and call its noteStopped callback. + + This method will be called automatically according to the midi data passed into + renderNextBlock(). Do not call it yourself, otherwise the internal MPE note state + will become inconsistent. + */ + virtual void noteReleased (MPENote finishedNote) override; + + /** Will find any voice that is currently playing changedNote, update its + currently playing note, and call its notePressureChanged method. + + This method will be called automatically according to the midi data passed into + renderNextBlock(). Do not call it yourself. + */ + virtual void notePressureChanged (MPENote changedNote) override; + + /** Will find any voice that is currently playing changedNote, update its + currently playing note, and call its notePitchbendChanged method. + + This method will be called automatically according to the midi data passed into + renderNextBlock(). Do not call it yourself. + */ + virtual void notePitchbendChanged (MPENote changedNote) override; + + /** Will find any voice that is currently playing changedNote, update its + currently playing note, and call its noteTimbreChanged method. + + This method will be called automatically according to the midi data passed into + renderNextBlock(). Do not call it yourself. + */ + virtual void noteTimbreChanged (MPENote changedNote) override; + + /** Will find any voice that is currently playing changedNote, update its + currently playing note, and call its noteKeyStateChanged method. + + This method will be called automatically according to the midi data passed into + renderNextBlock(). Do not call it yourself. + */ + virtual void noteKeyStateChanged (MPENote changedNote) override; + + //========================================================================== + /** This will simply call renderNextBlock for each currently active + voice and fill the buffer with the sum. + Override this method if you need to do more work to render your audio. + */ + virtual void renderNextSubBlock (AudioBuffer& outputAudio, + int startSample, + int numSamples) override; + + /** This will simply call renderNextBlock for each currently active + voice and fill the buffer with the sum. (souble-precision version) + Override this method if you need to do more work to render your audio. + */ + virtual void renderNextSubBlock (AudioBuffer& outputAudio, + int startSample, + int numSamples) override; + + //========================================================================== + /** Searches through the voices to find one that's not currently playing, and + which can play the given MPE note. + + If all voices are active and stealIfNoneAvailable is false, this returns + a nullptr. If all voices are active and stealIfNoneAvailable is true, + this will call findVoiceToSteal() to find a voice. + + If you need to find a free voice for something else than playing a note + (e.g. for deleting it), you can pass an invalid (default-constructed) MPENote. + */ + virtual MPESynthesiserVoice* findFreeVoice (MPENote noteToFindVoiceFor, + bool stealIfNoneAvailable) const; + + /** Chooses a voice that is most suitable for being re-used to play a new + note, or for being deleted by reduceNumVoices. + + 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. + + If you pass a valid MPENote for the optional argument, then the note number + of that note will be taken into account for finding the ideal voice to steal. + If you pass an invalid (default-constructed) MPENote instead, this part of + the algorithm will be ignored. + */ + virtual MPESynthesiserVoice* findVoiceToSteal (MPENote noteToStealVoiceFor = MPENote()) const; + + /** Starts a specified voice and tells it to play a particular MPENote. + You should never need to call this, it's called internally by + MPESynthesiserBase::instrument via the noteStarted callback, + but is protected in case it's useful for some custom subclasses. + */ + void startVoice (MPESynthesiserVoice* voice, MPENote noteToStart); + + /** Stops a given voice and tells it to stop playing a particular MPENote + (which should be the same note it is actually playing). + You should never need to call this, it's called internally by + MPESynthesiserBase::instrument via the noteReleased callback, + but is protected in case it's useful for some custom subclasses. + */ + void stopVoice (MPESynthesiserVoice* voice, MPENote noteToStop, bool allowTailOff); + + //========================================================================== + OwnedArray voices; + +private: + //========================================================================== + bool shouldStealVoices; + CriticalSection voicesLock; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPESynthesiser) +}; + + +#endif // JUCE_MPESynthesiser_H_INCLUDED diff --git a/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp b/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp new file mode 100644 index 000000000..44c679b83 --- /dev/null +++ b/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp @@ -0,0 +1,161 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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. + + ============================================================================== +*/ + +MPESynthesiserBase::MPESynthesiserBase() + : instrument (new MPEInstrument), + sampleRate (0), + minimumSubBlockSize (32) +{ + instrument->addListener (this); +} + +MPESynthesiserBase::MPESynthesiserBase (MPEInstrument* inst) + : instrument (inst), + sampleRate (0), + minimumSubBlockSize (32) +{ + jassert (instrument != nullptr); + instrument->addListener (this); +} + +//============================================================================== +MPEZoneLayout MPESynthesiserBase::getZoneLayout() const noexcept +{ + return instrument->getZoneLayout(); +} + +void MPESynthesiserBase::setZoneLayout (MPEZoneLayout newLayout) +{ + instrument->setZoneLayout (newLayout); +} + +//============================================================================== +void MPESynthesiserBase::enableLegacyMode (int pitchbendRange, Range channelRange) +{ + instrument->enableLegacyMode (pitchbendRange, channelRange); +} + +bool MPESynthesiserBase::isLegacyModeEnabled() const noexcept +{ + return instrument->isLegacyModeEnabled(); +} + +Range MPESynthesiserBase::getLegacyModeChannelRange() const noexcept +{ + return instrument->getLegacyModeChannelRange(); +} + +void MPESynthesiserBase::setLegacyModeChannelRange (Range channelRange) +{ + instrument->setLegacyModeChannelRange (channelRange); +} + +int MPESynthesiserBase::getLegacyModePitchbendRange() const noexcept +{ + return instrument->getLegacyModePitchbendRange(); +} + +void MPESynthesiserBase::setLegacyModePitchbendRange (int pitchbendRange) +{ + instrument->setLegacyModePitchbendRange (pitchbendRange); +} + +//============================================================================== +void MPESynthesiserBase::handleMidiEvent (const MidiMessage& m) +{ + instrument->processNextMidiEvent (m); +} + +//============================================================================== +template +void MPESynthesiserBase::renderNextBlock (AudioBuffer& outputAudio, + const MidiBuffer& inputMidi, + int startSample, + int numSamples) +{ + // you must set the sample rate before using this! + jassert (sampleRate != 0); + + MidiBuffer::Iterator midiIterator (inputMidi); + midiIterator.setNextSamplePosition (startSample); + + int midiEventPos; + MidiMessage m; + + const ScopedLock sl (renderAudioLock); + + while (numSamples > 0) + { + if (! midiIterator.getNextEvent (m, midiEventPos)) + { + renderNextSubBlock (outputAudio, startSample, numSamples); + return; + } + + const int samplesToNextMidiMessage = midiEventPos - startSample; + + if (samplesToNextMidiMessage >= numSamples) + { + renderNextSubBlock (outputAudio, startSample, numSamples); + handleMidiEvent (m); + break; + } + + if (samplesToNextMidiMessage < minimumSubBlockSize) + { + handleMidiEvent (m); + continue; + } + + renderNextSubBlock (outputAudio, startSample, samplesToNextMidiMessage); + handleMidiEvent (m); + startSample += samplesToNextMidiMessage; + numSamples -= samplesToNextMidiMessage; + } + + while (midiIterator.getNextEvent (m, midiEventPos)) + handleMidiEvent (m); +} + +// explicit instantiation for supported float types: +template void MPESynthesiserBase::renderNextBlock (AudioBuffer&, const MidiBuffer&, int, int); +template void MPESynthesiserBase::renderNextBlock (AudioBuffer&, const MidiBuffer&, int, int); + +//============================================================================== +void MPESynthesiserBase::setCurrentPlaybackSampleRate (const double newRate) +{ + if (sampleRate != newRate) + { + const ScopedLock sl (renderAudioLock); + instrument->releaseAllNotes(); + sampleRate = newRate; + } +} + +//============================================================================== +void MPESynthesiserBase::setMinimumRenderingSubdivisionSize (int numSamples) noexcept +{ + jassert (numSamples > 0); // it wouldn't make much sense for this to be less than 1 + minimumSubBlockSize = numSamples; +} diff --git a/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h b/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h new file mode 100644 index 000000000..7ea8abda8 --- /dev/null +++ b/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h @@ -0,0 +1,194 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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_MPESynthesiserBase_H_INCLUDED +#define JUCE_MPESynthesiserBase_H_INCLUDED + + +//============================================================================== +/** + Derive from this class to create a basic audio generator capable of MPE. + Implement the callbacks of MPEInstrument::Listener (noteAdded, notePressureChanged + etc.) to let your audio generator know that MPE notes were triggered, modulated, + or released. What to do inside them, and how that influences your audio generator, + is up to you! + + This class uses an instance of MPEInstrument internally to handle the MPE + note state logic. + + This class is a very low-level base class for an MPE instrument. If you need + something more sophisticated, have a look at MPESynthesiser. This class extends + MPESynthesiserBase by adding the concept of voices that can play notes, + a voice stealing algorithm, and much more. + + @see MPESynthesiser, MPEInstrument +*/ +struct JUCE_API MPESynthesiserBase : public MPEInstrument::Listener +{ +public: + //========================================================================== + /** Constructor. */ + MPESynthesiserBase(); + + /** Constructor. + + If you use this constructor, the synthesiser will take ownership of the + provided instrument object, and will use it internally to handle the + MPE note state logic. + This is useful if you want to use an instance of your own class derived + from MPEInstrument for the MPE logic. + */ + MPESynthesiserBase (MPEInstrument* instrument); + + //========================================================================== + /** Returns the synthesiser's internal MPE zone layout. + This happens by value, to enforce thread-safety and class invariants. + */ + MPEZoneLayout getZoneLayout() const noexcept; + + /** Re-sets the synthesiser's internal MPE zone layout to the one passed in. + As a side effect, this will discard all currently playing notes, + call noteReleased for all of them, and disable legacy mode (if previously enabled). + */ + void setZoneLayout (MPEZoneLayout newLayout); + + //========================================================================== + /** Tells the synthesiser what the sample rate is for the audio it's being + used to render. + */ + virtual void setCurrentPlaybackSampleRate (double sampleRate); + + /** 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; } + + //========================================================================== + /** Creates the next block of audio output. + + Call this to make sound. This will chop up the AudioBuffer into subBlock + pieces separated by events in the MIDI buffer, and then call + processNextSubBlock on each one of them. In between you will get calls + to noteAdded/Changed/Finished, where you can update parameters that + depend on those notes to use for your audio rendering. + */ + template + void renderNextBlock (AudioBuffer& outputAudio, + const MidiBuffer& inputMidi, + int startSample, + int numSamples); + + //========================================================================== + /** Handle incoming MIDI events (called from renderNextBlock). + + The default implementation provided here simply forwards everything + to MPEInstrument::processNextMidiEvent, where it is used to update the + MPE notes, zones etc. MIDI messages not relevant for MPE are ignored. + + This method can be overridden if you need to do custom MIDI handling + on top of MPE. The MPESynthesiser class overrides this to implement + callbacks for MIDI program changes and non-MPE-related MIDI controller + messages. + */ + virtual void handleMidiEvent (const MidiMessage&); + + //========================================================================== + /** Sets a minimum limit on the size to which audio sub-blocks will be divided when rendering. + + When rendering, the audio blocks that are passed into renderNextBlock() will be split up + into smaller blocks that lie between all the incoming midi messages, and it is these smaller + sub-blocks that are rendered with multiple calls to renderVoices(). + + Obviously in a pathological case where there are midi messages on every sample, then + renderVoices() could be called once per sample and lead to poor performance, so this + setting allows you to set a lower limit on the block size. + + The default setting is 32, which means that midi messages are accurate to about < 1ms + accuracy, which is probably fine for most purposes, but you may want to increase or + decrease this value for your synth. + */ + void setMinimumRenderingSubdivisionSize (int numSamples) noexcept; + + //========================================================================== + /** Puts the synthesiser into legacy mode. + + @param pitchbendRange The note pitchbend range in semitones to use when in legacy mode. + Must be between 0 and 96, otherwise behaviour is undefined. + The default pitchbend range in legacy mode is +/- 2 semitones. + @param channelRange The range of MIDI channels to use for notes when in legacy mode. + The default is to use all MIDI channels (1-16). + + To get out of legacy mode, set a new MPE zone layout using setZoneLayout. + */ + void enableLegacyMode (int pitchbendRange = 2, + Range channelRange = Range (1, 17)); + + /** Returns true if the instrument is in legacy mode, false otherwise. */ + bool isLegacyModeEnabled() const noexcept; + + /** Returns the range of MIDI channels (1-16) to be used for notes when in legacy mode. */ + Range getLegacyModeChannelRange() const noexcept; + + /** Re-sets the range of MIDI channels (1-16) to be used for notes when in legacy mode. */ + void setLegacyModeChannelRange (Range channelRange); + + /** Returns the pitchbend range in semitones (0-96) to be used for notes when in legacy mode. */ + int getLegacyModePitchbendRange() const noexcept; + + /** Re-sets the pitchbend range in semitones (0-96) to be used for notes when in legacy mode. */ + void setLegacyModePitchbendRange (int pitchbendRange); + +protected: + //========================================================================== + /** Implement this method to render your audio inside. + @see renderNextBlock + */ + virtual void renderNextSubBlock (AudioBuffer& outputAudio, + int startSample, + int numSamples) = 0; + + /** Implement this method if you want to render 64-bit audio as well; + otherwise leave blank. + */ + virtual void renderNextSubBlock (AudioBuffer& /*outputAudio*/, + int /*startSample*/, + int /*numSamples*/) {} + +protected: + //========================================================================== + /** @internal */ + ScopedPointer instrument; + /** @internal */ + CriticalSection renderAudioLock; + +private: + //========================================================================== + double sampleRate; + int minimumSubBlockSize; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPESynthesiserBase) +}; + + +#endif // JUCE_MPESynthesiserBase_H_INCLUDED diff --git a/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.cpp b/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.cpp new file mode 100644 index 000000000..bb03399c8 --- /dev/null +++ b/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.cpp @@ -0,0 +1,53 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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. + + ============================================================================== +*/ + +MPESynthesiserVoice::MPESynthesiserVoice() + : currentSampleRate (0), noteStartTime (0) +{ +} + +MPESynthesiserVoice::~MPESynthesiserVoice() +{ +} + +//============================================================================== +bool MPESynthesiserVoice::isCurrentlyPlayingNote (MPENote note) const noexcept +{ + return isActive() && currentlyPlayingNote.noteID == note.noteID; +} + +bool MPESynthesiserVoice::isPlayingButReleased() const noexcept +{ + return isActive() && currentlyPlayingNote.keyState == MPENote::off; +} + +bool MPESynthesiserVoice::wasStartedBefore (const MPESynthesiserVoice& other) const noexcept +{ + return noteStartTime < other.noteStartTime; +} + +void MPESynthesiserVoice::clearCurrentNote() noexcept +{ + currentlyPlayingNote = MPENote(); +} diff --git a/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.h b/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.h new file mode 100644 index 000000000..8fa3e1262 --- /dev/null +++ b/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.h @@ -0,0 +1,191 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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_MPEVoice_H_INCLUDED +#define JUCE_MPEVoice_H_INCLUDED + +//============================================================================== +/** + Represents an MPE voice that an MPESynthesiser can use to play a sound. + + A voice plays a single sound at a time, and a synthesiser holds an array of + voices so that it can play polyphonically. + + @see MPESynthesiser, MPENote + */ +class JUCE_API MPESynthesiserVoice +{ +public: + //======================================================================== + /** Constructor. */ + MPESynthesiserVoice(); + + /** Destructor. */ + virtual ~MPESynthesiserVoice(); + + /** Returns the MPENote that this voice is currently playing. + Returns an invalid MPENote if no note is playing + (you can check this using MPENote::isValid() or MPEVoice::isActive()). + */ + MPENote getCurrentlyPlayingNote() const noexcept { return currentlyPlayingNote; } + + /** Returns true if the voice is currently playing the given MPENote + (as identified by the note's initial note number and MIDI channel). + */ + bool isCurrentlyPlayingNote (MPENote note) const noexcept; + + /** Returns true if this voice is currently busy playing a sound. + By default this just checks whether getCurrentlyPlayingNote() + returns a valid MPE note, but can be overridden for more advanced checking. + */ + virtual bool isActive() const { return currentlyPlayingNote.isValid(); } + + /** Returns true if a voice is sounding in its release phase. **/ + bool isPlayingButReleased() const noexcept; + + /** Called by the MPESynthesiser to let the voice know that a new note has started on it. + This will be called during the rendering callback, so must be fast and thread-safe. + */ + virtual void noteStarted() = 0; + + /** Called by the MPESynthesiser to let the voice know that its currently playing note has stopped. + This will be called during the rendering callback, so must be fast and thread-safe. + + 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 noteStopped (bool allowTailOff) = 0; + + /** Called by the MPESynthesiser to let the voice know that its currently playing note + has changed its pressure value. + This will be called during the rendering callback, so must be fast and thread-safe. + */ + virtual void notePressureChanged() = 0; + + /** Called by the MPESynthesiser to let the voice know that its currently playing note + has changed its pitchbend value. + This will be called during the rendering callback, so must be fast and thread-safe. + + Note: You can call currentlyPlayingNote.getFrequencyInHertz() to find out the effective frequency + of the note, as a sum of the initial note number, the per-note pitchbend and the master pitchbend. + */ + virtual void notePitchbendChanged() = 0; + + /** Called by the MPESynthesiser to let the voice know that its currently playing note + has changed its timbre value. + This will be called during the rendering callback, so must be fast and thread-safe. + */ + virtual void noteTimbreChanged() = 0; + + /** Called by the MPESynthesiser to let the voice know that its currently playing note + has changed its key state. + This typically happens when a sustain or sostenuto pedal is pressed or released (on + an MPE channel relevant for this note), or if the note key is lifted while the sustained + or sostenuto pedal is still held down. + This will be called during the rendering callback, so must be fast and thread-safe. + */ + virtual void noteKeyStateChanged() = 0; + + /** 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 (AudioBuffer& outputBuffer, + int startSample, + int numSamples) = 0; + + /** Renders the next block of 64-bit data for this voice. + + Support for 64-bit audio is optional. You can choose to not override this method if + you don't need it (the default implementation simply does nothing). + */ + virtual void renderNextBlock (AudioBuffer& /*outputBuffer*/, + int /*startSample*/, + int /*numSamples*/) {} + + /** 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 setCurrentSampleRate (double newRate) { currentSampleRate = newRate; } + + /** Returns the current target sample rate at which rendering is being done. + Subclasses may need to know this so that they can pitch things correctly. + */ + double getSampleRate() const noexcept { return currentSampleRate; } + + /** Returns true if this voice started playing its current note before the other voice did. */ + bool wasStartedBefore (const MPESynthesiserVoice& 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() noexcept; + + //========================================================================== + double currentSampleRate; + MPENote currentlyPlayingNote; + +private: + //========================================================================== + friend class MPESynthesiser; + uint32 noteStartTime; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPESynthesiserVoice) +}; + + +#endif // JUCE_MPEVoice_H_INCLUDED diff --git a/source/modules/juce_audio_basics/mpe/juce_MPEValue.cpp b/source/modules/juce_audio_basics/mpe/juce_MPEValue.cpp new file mode 100644 index 000000000..c1981fa75 --- /dev/null +++ b/source/modules/juce_audio_basics/mpe/juce_MPEValue.cpp @@ -0,0 +1,170 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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. + + ============================================================================== +*/ + +MPEValue::MPEValue() noexcept : normalisedValue (8192) +{ +} + +MPEValue::MPEValue (int value) : normalisedValue (value) +{ +} + +//============================================================================== +MPEValue MPEValue::from7BitInt (int value) noexcept +{ + jassert (value >= 0 && value <= 127); + + const int valueAs14Bit = value <= 64 ? value << 7 : int (jmap (float (value - 64), 0.0f, 63.0f, 0.0f, 8191.0f)) + 8192; + return MPEValue (valueAs14Bit); +} + +MPEValue MPEValue::from14BitInt (int value) noexcept +{ + jassert (value >= 0 && value <= 16383); + return MPEValue (value); +} + +//============================================================================== +MPEValue MPEValue::minValue() noexcept { return MPEValue::from7BitInt (0); } +MPEValue MPEValue::centreValue() noexcept { return MPEValue::from7BitInt (64); } +MPEValue MPEValue::maxValue() noexcept { return MPEValue::from7BitInt (127); } + +int MPEValue::as7BitInt() const noexcept +{ + return normalisedValue >> 7; +} + +int MPEValue::as14BitInt() const noexcept +{ + return normalisedValue; +} + +//============================================================================== +float MPEValue::asSignedFloat() const noexcept +{ + return (normalisedValue < 8192) + ? jmap (float (normalisedValue), 0.0f, 8192.0f, -1.0f, 0.0f) + : jmap (float (normalisedValue), 8192.0f, 16383.0f, 0.0f, 1.0f); +} + +float MPEValue::asUnsignedFloat() const noexcept +{ + return jmap (float (normalisedValue), 0.0f, 16383.0f, 0.0f, 1.0f); +} + +//============================================================================== +bool MPEValue::operator== (const MPEValue& other) const noexcept +{ + return normalisedValue == other.normalisedValue; +} + +bool MPEValue::operator!= (const MPEValue& other) const noexcept +{ + return ! operator== (other); +} + +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +class MPEValueTests : public UnitTest +{ +public: + MPEValueTests() : UnitTest ("MPEValue class") {} + + void runTest() override + { + beginTest ("comparison operator"); + { + MPEValue value1 = MPEValue::from7BitInt (7); + MPEValue value2 = MPEValue::from7BitInt (7); + MPEValue value3 = MPEValue::from7BitInt (8); + + expect (value1 == value1); + expect (value1 == value2); + expect (value1 != value3); + } + + beginTest ("special values"); + { + expectEquals (MPEValue::minValue().as7BitInt(), 0); + expectEquals (MPEValue::minValue().as14BitInt(), 0); + + expectEquals (MPEValue::centreValue().as7BitInt(), 64); + expectEquals (MPEValue::centreValue().as14BitInt(), 8192); + + expectEquals (MPEValue::maxValue().as7BitInt(), 127); + expectEquals (MPEValue::maxValue().as14BitInt(), 16383); + } + + beginTest ("zero/minimum value"); + { + expectValuesConsistent (MPEValue::from7BitInt (0), 0, 0, -1.0f, 0.0f); + expectValuesConsistent (MPEValue::from14BitInt (0), 0, 0, -1.0f, 0.0f); + } + + beginTest ("maximum value"); + { + expectValuesConsistent (MPEValue::from7BitInt (127), 127, 16383, 1.0f, 1.0f); + expectValuesConsistent (MPEValue::from14BitInt (16383), 127, 16383, 1.0f, 1.0f); + } + + beginTest ("centre value"); + { + expectValuesConsistent (MPEValue::from7BitInt (64), 64, 8192, 0.0f, 0.5f); + expectValuesConsistent (MPEValue::from14BitInt (8192), 64, 8192, 0.0f, 0.5f); + } + + beginTest ("value halfway between min and centre"); + { + expectValuesConsistent (MPEValue::from7BitInt (32), 32, 4096, -0.5f, 0.25f); + expectValuesConsistent (MPEValue::from14BitInt (4096), 32, 4096, -0.5f, 0.25f); + } + } + +private: + //========================================================================== + void expectValuesConsistent (MPEValue value, + int expectedValueAs7BitInt, + int expectedValueAs14BitInt, + float expectedValueAsSignedFloat, + float expectedValueAsUnsignedFloat) + { + expectEquals (value.as7BitInt(), expectedValueAs7BitInt); + expectEquals (value.as14BitInt(), expectedValueAs14BitInt); + expectFloatWithinRelativeError (value.asSignedFloat(), expectedValueAsSignedFloat, 0.0001f); + expectFloatWithinRelativeError (value.asUnsignedFloat(), expectedValueAsUnsignedFloat, 0.0001f); + } + + //========================================================================== + void expectFloatWithinRelativeError (float actualValue, float expectedValue, float maxRelativeError) + { + const float maxAbsoluteError = jmax (1.0f, std::fabs (expectedValue)) * maxRelativeError; + expect (std::fabs (expectedValue - actualValue) < maxAbsoluteError); + } +}; + +static MPEValueTests MPEValueUnitTests; + +#endif // JUCE_UNIT_TESTS diff --git a/source/modules/juce_audio_basics/mpe/juce_MPEValue.h b/source/modules/juce_audio_basics/mpe/juce_MPEValue.h new file mode 100644 index 000000000..e137c46e2 --- /dev/null +++ b/source/modules/juce_audio_basics/mpe/juce_MPEValue.h @@ -0,0 +1,96 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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_MPEVALUE_H_INCLUDED +#define JUCE_MPEVALUE_H_INCLUDED + + +//============================================================================== +/** + This class represents a single value for any of the MPE + dimensions of control. It supports values with 7-bit or 14-bit resolutions + (corresponding to 1 or 2 MIDI bytes, respectively). It also offers helper + functions to query the value in a variety of representations that can be + useful in an audio or MIDI context. +*/ +class JUCE_API MPEValue +{ +public: + //========================================================================== + /** Default constructor. Constructs an MPEValue corresponding + to the centre value. + */ + MPEValue() noexcept; + + /** Constructs an MPEValue from an integer between 0 and 127 + (using 7-bit precision). + */ + static MPEValue from7BitInt (int value) noexcept; + + /** Constructs an MPEValue from an integer between 0 and 16383 + (using 14-bit precision). + */ + static MPEValue from14BitInt (int value) noexcept; + + /** Constructs an MPEValue corresponding to the centre value. */ + static MPEValue centreValue() noexcept; + + /** Constructs an MPEValue corresponding to the minimum value. */ + static MPEValue minValue() noexcept; + + /** Constructs an MPEValue corresponding to the maximum value. */ + static MPEValue maxValue() noexcept; + + /** Retrieves the current value as an integer between 0 and 127. + Information will be lost if the value was initialised with a precision + higher than 7-bit. + */ + int as7BitInt() const noexcept; + + /** Retrieves the current value as an integer between 0 and 16383. + Resolution will be lost if the value was initialised with a precision + higher than 14-bit. + */ + int as14BitInt() const noexcept; + + /** Retrieves the current value mapped to a float between -1.0f and 1.0f. */ + float asSignedFloat() const noexcept; + + /** Retrieves the current value mapped to a float between 0.0f and 1.0f. */ + float asUnsignedFloat() const noexcept; + + /** Returns true if two values are equal. */ + bool operator== (const MPEValue& other) const noexcept; + + /** Returns true if two values are not equal. */ + bool operator!= (const MPEValue& other) const noexcept; + +private: + //========================================================================== + MPEValue (int normalisedValue); + int normalisedValue; +}; + + +#endif // JUCE_MPEVALUE_H_INCLUDED diff --git a/source/modules/juce_audio_basics/mpe/juce_MPEZone.cpp b/source/modules/juce_audio_basics/mpe/juce_MPEZone.cpp new file mode 100644 index 000000000..54b2fc2b4 --- /dev/null +++ b/source/modules/juce_audio_basics/mpe/juce_MPEZone.cpp @@ -0,0 +1,302 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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 +{ + void checkAndLimitZoneParameters (int minValue, + int maxValue, + int& valueToCheckAndLimit) noexcept + { + if (valueToCheckAndLimit < minValue || valueToCheckAndLimit > maxValue) + { + // if you hit this, one of the parameters you supplied for MPEZone + // was not within the allowed range! + // we fit this back into the allowed range here to maintain a valid + // state for the zone, but probably the resulting zone is not what you + //wanted it to be! + jassertfalse; + + valueToCheckAndLimit = jlimit (minValue, maxValue, valueToCheckAndLimit); + } + } +} + +//============================================================================== +MPEZone::MPEZone (int masterChannel_, + int numNoteChannels_, + int perNotePitchbendRange_, + int masterPitchbendRange_) noexcept + : masterChannel (masterChannel_), + numNoteChannels (numNoteChannels_), + perNotePitchbendRange (perNotePitchbendRange_), + masterPitchbendRange (masterPitchbendRange_) +{ + checkAndLimitZoneParameters (1, 15, masterChannel); + checkAndLimitZoneParameters (1, 16 - masterChannel, numNoteChannels); + checkAndLimitZoneParameters (0, 96, perNotePitchbendRange); + checkAndLimitZoneParameters (0, 96, masterPitchbendRange); +} + +//============================================================================== +int MPEZone::getMasterChannel() const noexcept +{ + return masterChannel; +} + +int MPEZone::getNumNoteChannels() const noexcept +{ + return numNoteChannels; +} + +int MPEZone::getFirstNoteChannel() const noexcept +{ + return masterChannel + 1; +} + +int MPEZone::getLastNoteChannel() const noexcept +{ + return masterChannel + numNoteChannels; +} + +Range MPEZone::getNoteChannelRange() const noexcept +{ + return Range::withStartAndLength (getFirstNoteChannel(), getNumNoteChannels()); +} + +bool MPEZone::isUsingChannel (int channel) const noexcept +{ + jassert (channel > 0 && channel <= 16); + return channel >= masterChannel && channel <= masterChannel + numNoteChannels; +} + +bool MPEZone::isUsingChannelAsNoteChannel (int channel) const noexcept +{ + jassert (channel > 0 && channel <= 16); + return channel > masterChannel && channel <= masterChannel + numNoteChannels; +} + +int MPEZone::getPerNotePitchbendRange() const noexcept +{ + return perNotePitchbendRange; +} + +int MPEZone::getMasterPitchbendRange() const noexcept +{ + return masterPitchbendRange; +} + +void MPEZone::setPerNotePitchbendRange (int rangeInSemitones) noexcept +{ + checkAndLimitZoneParameters (0, 96, rangeInSemitones); + perNotePitchbendRange = rangeInSemitones; +} + +void MPEZone::setMasterPitchbendRange (int rangeInSemitones) noexcept +{ + checkAndLimitZoneParameters (0, 96, rangeInSemitones); + masterPitchbendRange = rangeInSemitones; +} + +//============================================================================== +bool MPEZone::overlapsWith (MPEZone other) const noexcept +{ + if (masterChannel == other.masterChannel) + return true; + + if (masterChannel > other.masterChannel) + return other.overlapsWith (*this); + + return masterChannel + numNoteChannels >= other.masterChannel; +} + +//============================================================================== +bool MPEZone::truncateToFit (MPEZone other) noexcept +{ + const int masterChannelDiff = other.masterChannel - masterChannel; + + // we need at least 2 channels to be left after truncation: + // 1 master channel and 1 note channel. otherwise we can't truncate. + if (masterChannelDiff < 2) + return false; + + numNoteChannels = jmin (numNoteChannels, masterChannelDiff - 1); + return true; +} + +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +class MPEZoneTests : public UnitTest +{ +public: + MPEZoneTests() : UnitTest ("MPEZone class") {} + + void runTest() override + { + beginTest ("initialisation"); + { + { + MPEZone zone (1, 10); + + expectEquals (zone.getMasterChannel(), 1); + expectEquals (zone.getNumNoteChannels(), 10); + expectEquals (zone.getFirstNoteChannel(), 2); + expectEquals (zone.getLastNoteChannel(), 11); + expectEquals (zone.getPerNotePitchbendRange(), 48); + expectEquals (zone.getMasterPitchbendRange(), 2); + + expect (zone.isUsingChannel (1)); + expect (zone.isUsingChannel (2)); + expect (zone.isUsingChannel (10)); + expect (zone.isUsingChannel (11)); + expect (! zone.isUsingChannel (12)); + expect (! zone.isUsingChannel (16)); + + expect (! zone.isUsingChannelAsNoteChannel (1)); + expect (zone.isUsingChannelAsNoteChannel (2)); + expect (zone.isUsingChannelAsNoteChannel (10)); + expect (zone.isUsingChannelAsNoteChannel (11)); + expect (! zone.isUsingChannelAsNoteChannel (12)); + expect (! zone.isUsingChannelAsNoteChannel (16)); + } + { + MPEZone zone (5, 4); + + expectEquals (zone.getMasterChannel(), 5); + expectEquals (zone.getNumNoteChannels(), 4); + expectEquals (zone.getFirstNoteChannel(), 6); + expectEquals (zone.getLastNoteChannel(), 9); + expectEquals (zone.getPerNotePitchbendRange(), 48); + expectEquals (zone.getMasterPitchbendRange(), 2); + + expect (! zone.isUsingChannel (1)); + expect (! zone.isUsingChannel (4)); + expect (zone.isUsingChannel (5)); + expect (zone.isUsingChannel (6)); + expect (zone.isUsingChannel (8)); + expect (zone.isUsingChannel (9)); + expect (! zone.isUsingChannel (10)); + expect (! zone.isUsingChannel (16)); + + expect (! zone.isUsingChannelAsNoteChannel (5)); + expect (zone.isUsingChannelAsNoteChannel (6)); + expect (zone.isUsingChannelAsNoteChannel (8)); + expect (zone.isUsingChannelAsNoteChannel (9)); + expect (! zone.isUsingChannelAsNoteChannel (10)); + } + + } + + beginTest ("getNoteChannelRange"); + { + MPEZone zone (2, 10); + + Range noteChannelRange = zone.getNoteChannelRange(); + expectEquals (noteChannelRange.getStart(), 3); + expectEquals (noteChannelRange.getEnd(), 13); + } + + beginTest ("setting master pitchbend range"); + { + MPEZone zone (1, 10); + + zone.setMasterPitchbendRange (96); + expectEquals (zone.getMasterPitchbendRange(), 96); + zone.setMasterPitchbendRange (0); + expectEquals (zone.getMasterPitchbendRange(), 0); + + expectEquals (zone.getPerNotePitchbendRange(), 48); + } + + beginTest ("setting per-note pitchbend range"); + { + MPEZone zone (1, 10); + + zone.setPerNotePitchbendRange (96); + expectEquals (zone.getPerNotePitchbendRange(), 96); + zone.setPerNotePitchbendRange (0); + expectEquals (zone.getPerNotePitchbendRange(), 0); + + expectEquals (zone.getMasterPitchbendRange(), 2); + } + + beginTest ("checking overlap"); + { + testOverlapsWith (1, 10, 1, 10, true); + testOverlapsWith (1, 4, 6, 3, false); + testOverlapsWith (1, 4, 8, 3, false); + testOverlapsWith (2, 10, 2, 8, true); + testOverlapsWith (1, 10, 3, 2, true); + testOverlapsWith (3, 10, 5, 9, true); + } + + beginTest ("truncating"); + { + testTruncateToFit (1, 10, 3, 10, true, 1, 1); + testTruncateToFit (3, 10, 1, 10, false, 3, 10); + testTruncateToFit (1, 10, 5, 8, true, 1, 3); + testTruncateToFit (5, 8, 1, 10, false, 5, 8); + testTruncateToFit (1, 10, 4, 3, true, 1, 2); + testTruncateToFit (4, 3, 1, 10, false, 4, 3); + testTruncateToFit (1, 3, 5, 3, true, 1, 3); + testTruncateToFit (5, 3, 1, 3, false, 5, 3); + testTruncateToFit (1, 3, 7, 3, true, 1, 3); + testTruncateToFit (7, 3, 1, 3, false, 7, 3); + testTruncateToFit (1, 10, 2, 10, false, 1, 10); + testTruncateToFit (2, 10, 1, 10, false, 2, 10); + } + } + +private: + //========================================================================== + void testOverlapsWith (int masterChannelFirst, int numNoteChannelsFirst, + int masterChannelSecond, int numNoteChannelsSecond, + bool expectedRetVal) + { + MPEZone first (masterChannelFirst, numNoteChannelsFirst); + MPEZone second (masterChannelSecond, numNoteChannelsSecond); + + expect (first.overlapsWith (second) == expectedRetVal); + expect (second.overlapsWith (first) == expectedRetVal); + } + + //========================================================================== + void testTruncateToFit (int masterChannelFirst, int numNoteChannelsFirst, + int masterChannelSecond, int numNoteChannelsSecond, + bool expectedRetVal, + int masterChannelFirstAfter, int numNoteChannelsFirstAfter) + { + MPEZone first (masterChannelFirst, numNoteChannelsFirst); + MPEZone second (masterChannelSecond, numNoteChannelsSecond); + + expect (first.truncateToFit (second) == expectedRetVal); + expectEquals (first.getMasterChannel(), masterChannelFirstAfter); + expectEquals (first.getNumNoteChannels(), numNoteChannelsFirstAfter); + } +}; + +static MPEZoneTests MPEZoneUnitTests; + +#endif // JUCE_UNIT_TESTS diff --git a/source/modules/juce_audio_basics/mpe/juce_MPEZone.h b/source/modules/juce_audio_basics/mpe/juce_MPEZone.h new file mode 100644 index 000000000..b0471e256 --- /dev/null +++ b/source/modules/juce_audio_basics/mpe/juce_MPEZone.h @@ -0,0 +1,132 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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_MPEZONE_H_INCLUDED +#define JUCE_MPEZONE_H_INCLUDED + + +//============================================================================== +/** + This struct represents an MPE Zone. + + An MPE Zone occupies one master MIDI channel and an arbitrary + number of note channels that immediately follow the master channel. + It also defines a pitchbend range (in semitones) to be applied for per-note + pitchbends and master pitchbends, respectively. + + @see MPEZoneLayout +*/ +struct JUCE_API MPEZone +{ + /** Constructor. + Creates an MPE zone with the given master channel and + number of note channels. + + @param masterChannel The master MIDI channel of the new zone. + All master (not per-note) messages should be send to this channel. + Must be between 1 and 15. Otherwise, the behaviour + is undefined. + + @param numNoteChannels The number of note channels that the new zone + should use. The first note channel will be one higher + than the master channel. The number of note channels + must be at least 1 and no greater than 16 - masterChannel. + Otherwise, the behaviour is undefined. + + @param perNotePitchbendRange The per-note pitchbend range in semitones of the new zone. + Must be between 0 and 96. Otherwise the behaviour is undefined. + If unspecified, the default setting of +/- 48 semitones + will be used. + + @param masterPitchbendRange The master pitchbend range in semitones of the new zone. + Must be between 0 and 96. Otherwise the behaviour is undefined. + If unspecified, the default setting of +/- 2 semitones + will be used. + */ + MPEZone (int masterChannel, + int numNoteChannels, + int perNotePitchbendRange = 48, + int masterPitchbendRange = 2) noexcept; + + /* Returns the MIDI master channel of this zone. */ + int getMasterChannel() const noexcept; + + /** Returns the number of note channels occupied by this zone. */ + int getNumNoteChannels() const noexcept; + + /* Returns the MIDI channel number of the lowest-numbered note channel of this zone. */ + int getFirstNoteChannel() const noexcept; + + /* Returns the MIDI channel number of the highest-numbered note channel of this zone. */ + int getLastNoteChannel() const noexcept; + + /** Returns the MIDI channel numbers of the note channels of this zone as a Range. */ + Range getNoteChannelRange() const noexcept; + + /** Returns true if the MIDI channel (in the range 1-16) is used by this zone + either as a note channel or as the master channel; false otherwise. */ + bool isUsingChannel (int channel) const noexcept; + + /** Returns true if the MIDI channel (in the range 1-16) is used by this zone + as a note channel; false otherwise. */ + bool isUsingChannelAsNoteChannel (int channel) const noexcept; + + /** Returns the per-note pitchbend range in semitones set for this zone. */ + int getPerNotePitchbendRange() const noexcept; + + /** Returns the master pitchbend range in semitones set for this zone. */ + int getMasterPitchbendRange() const noexcept; + + /** Sets the per-note pitchbend range in semitones for this zone. */ + void setPerNotePitchbendRange (int rangeInSemitones) noexcept; + + /** Sets the master pitchbend range in semitones for this zone. */ + void setMasterPitchbendRange (int rangeInSemitones) noexcept; + + /** Returns true if the MIDI channels occupied by this zone + overlap with those occupied by the other zone. + */ + bool overlapsWith (MPEZone other) const noexcept; + + /** Tries to truncate this zone in such a way that the range of MIDI channels + it occupies do not overlap with the other zone, by reducing this zone's + number of note channels. + + @returns true if the truncation succeeded or if no truncation is necessary + because the zones do not overlap. False if the zone cannot be truncated + in a way that would remove the overlap (in this case you need to delete + the zone to remove the overlap). + */ + bool truncateToFit (MPEZone zoneToAvoid) noexcept; + +private: + //========================================================================== + int masterChannel; + int numNoteChannels; + int perNotePitchbendRange; + int masterPitchbendRange; +}; + + +#endif // JUCE_MPEZONE_H_INCLUDED diff --git a/source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp b/source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp new file mode 100644 index 000000000..3f8c2b6b4 --- /dev/null +++ b/source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp @@ -0,0 +1,346 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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. + + ============================================================================== +*/ + +MPEZoneLayout::MPEZoneLayout() noexcept +{ +} + +//============================================================================== +bool MPEZoneLayout::addZone (MPEZone newZone) +{ + bool noOtherZonesModified = true; + + for (int i = zones.size(); --i >= 0;) + { + MPEZone& zone = zones.getReference (i); + + if (zone.overlapsWith (newZone)) + { + if (! zone.truncateToFit (newZone)) + zones.removeRange (i, 1); + // can't use zones.remove (i) because that requires a default c'tor :-( + + noOtherZonesModified = false; + } + } + + zones.add (newZone); + return noOtherZonesModified; +} + +//============================================================================== +int MPEZoneLayout::getNumZones() const noexcept +{ + return zones.size(); +} + +MPEZone* MPEZoneLayout::getZoneByIndex (int index) const noexcept +{ + if (zones.size() < index) + return nullptr; + + return &(zones.getReference (index)); +} + +void MPEZoneLayout::clearAllZones() +{ + zones.clear(); +} + +//============================================================================== +void MPEZoneLayout::processNextMidiEvent (const MidiMessage& message) +{ + if (! message.isController()) + return; + + MidiRPNMessage rpn; + + if (rpnDetector.parseControllerMessage (message.getChannel(), + message.getControllerNumber(), + message.getControllerValue(), + rpn)) + { + processRpnMessage (rpn); + } +} + +void MPEZoneLayout::processRpnMessage (MidiRPNMessage rpn) +{ + if (rpn.parameterNumber == MPEMessages::zoneLayoutMessagesRpnNumber) + processZoneLayoutRpnMessage (rpn); + else if (rpn.parameterNumber == 0) + processPitchbendRangeRpnMessage (rpn); +} + +void MPEZoneLayout::processZoneLayoutRpnMessage (MidiRPNMessage rpn) +{ + if (rpn.value < 16) + addZone (MPEZone (rpn.channel - 1, rpn.value)); + else + clearAllZones(); +} + +//============================================================================== +void MPEZoneLayout::processPitchbendRangeRpnMessage (MidiRPNMessage rpn) +{ + if (MPEZone* zone = getZoneByFirstNoteChannel (rpn.channel)) + { + zone->setPerNotePitchbendRange (rpn.value); + return; + } + + if (MPEZone* zone = getZoneByMasterChannel (rpn.channel)) + zone->setMasterPitchbendRange (rpn.value); +} + +//============================================================================== +void MPEZoneLayout::processNextMidiBuffer (const MidiBuffer& buffer) +{ + MidiBuffer::Iterator iter (buffer); + MidiMessage message; + int samplePosition; // not actually used, so no need to initialise. + + while (iter.getNextEvent (message, samplePosition)) + processNextMidiEvent (message); +} + +//============================================================================== +MPEZone* MPEZoneLayout::getZoneByChannel (int channel) const noexcept +{ + for (MPEZone* zone = zones.begin(); zone != zones.end(); ++zone) + if (zone->isUsingChannel (channel)) + return zone; + + return nullptr; +} + +MPEZone* MPEZoneLayout::getZoneByMasterChannel (int channel) const noexcept +{ + for (MPEZone* zone = zones.begin(); zone != zones.end(); ++zone) + if (zone->getMasterChannel() == channel) + return zone; + + return nullptr; +} + +MPEZone* MPEZoneLayout::getZoneByFirstNoteChannel (int channel) const noexcept +{ + for (MPEZone* zone = zones.begin(); zone != zones.end(); ++zone) + if (zone->getFirstNoteChannel() == channel) + return zone; + + return nullptr; +} + +MPEZone* MPEZoneLayout::getZoneByNoteChannel (int channel) const noexcept +{ + for (MPEZone* zone = zones.begin(); zone != zones.end(); ++zone) + if (zone->isUsingChannelAsNoteChannel (channel)) + return zone; + + return nullptr; +} + +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + + +class MPEZoneLayoutTests : public UnitTest +{ +public: + MPEZoneLayoutTests() : UnitTest ("MPEZoneLayout class") {} + + void runTest() override + { + beginTest ("initialisation"); + { + MPEZoneLayout layout; + expectEquals (layout.getNumZones(), 0); + } + + beginTest ("adding zones"); + { + MPEZoneLayout layout; + + expect (layout.addZone (MPEZone (1, 7))); + + expectEquals (layout.getNumZones(), 1); + expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1); + expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 7); + + expect (layout.addZone (MPEZone (9, 7))); + + expectEquals (layout.getNumZones(), 2); + expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1); + expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 7); + expectEquals (layout.getZoneByIndex (1)->getMasterChannel(), 9); + expectEquals (layout.getZoneByIndex (1)->getNumNoteChannels(), 7); + + expect (! layout.addZone (MPEZone (5, 3))); + + expectEquals (layout.getNumZones(), 3); + expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1); + expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 3); + expectEquals (layout.getZoneByIndex (1)->getMasterChannel(), 9); + expectEquals (layout.getZoneByIndex (1)->getNumNoteChannels(), 7); + expectEquals (layout.getZoneByIndex (2)->getMasterChannel(), 5); + expectEquals (layout.getZoneByIndex (2)->getNumNoteChannels(), 3); + + expect (! layout.addZone (MPEZone (5, 4))); + + expectEquals (layout.getNumZones(), 2); + expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1); + expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 3); + expectEquals (layout.getZoneByIndex (1)->getMasterChannel(), 5); + expectEquals (layout.getZoneByIndex (1)->getNumNoteChannels(), 4); + + expect (! layout.addZone (MPEZone (6, 4))); + + expectEquals (layout.getNumZones(), 2); + expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1); + expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 3); + expectEquals (layout.getZoneByIndex (1)->getMasterChannel(), 6); + expectEquals (layout.getZoneByIndex (1)->getNumNoteChannels(), 4); + } + + beginTest ("querying zones"); + { + MPEZoneLayout layout; + + layout.addZone (MPEZone (2, 5)); + layout.addZone (MPEZone (9, 4)); + + expect (layout.getZoneByMasterChannel (1) == nullptr); + expect (layout.getZoneByMasterChannel (2) != nullptr); + expect (layout.getZoneByMasterChannel (3) == nullptr); + expect (layout.getZoneByMasterChannel (8) == nullptr); + expect (layout.getZoneByMasterChannel (9) != nullptr); + expect (layout.getZoneByMasterChannel (10) == nullptr); + + expectEquals (layout.getZoneByMasterChannel (2)->getNumNoteChannels(), 5); + expectEquals (layout.getZoneByMasterChannel (9)->getNumNoteChannels(), 4); + + expect (layout.getZoneByFirstNoteChannel (2) == nullptr); + expect (layout.getZoneByFirstNoteChannel (3) != nullptr); + expect (layout.getZoneByFirstNoteChannel (4) == nullptr); + expect (layout.getZoneByFirstNoteChannel (9) == nullptr); + expect (layout.getZoneByFirstNoteChannel (10) != nullptr); + expect (layout.getZoneByFirstNoteChannel (11) == nullptr); + + expectEquals (layout.getZoneByFirstNoteChannel (3)->getNumNoteChannels(), 5); + expectEquals (layout.getZoneByFirstNoteChannel (10)->getNumNoteChannels(), 4); + + expect (layout.getZoneByNoteChannel (2) == nullptr); + expect (layout.getZoneByNoteChannel (3) != nullptr); + expect (layout.getZoneByNoteChannel (4) != nullptr); + expect (layout.getZoneByNoteChannel (6) != nullptr); + expect (layout.getZoneByNoteChannel (7) != nullptr); + expect (layout.getZoneByNoteChannel (8) == nullptr); + expect (layout.getZoneByNoteChannel (9) == nullptr); + expect (layout.getZoneByNoteChannel (10) != nullptr); + expect (layout.getZoneByNoteChannel (11) != nullptr); + expect (layout.getZoneByNoteChannel (12) != nullptr); + expect (layout.getZoneByNoteChannel (13) != nullptr); + expect (layout.getZoneByNoteChannel (14) == nullptr); + + expectEquals (layout.getZoneByNoteChannel (5)->getNumNoteChannels(), 5); + expectEquals (layout.getZoneByNoteChannel (13)->getNumNoteChannels(), 4); + } + + beginTest ("clear all zones"); + { + MPEZoneLayout layout; + + expect (layout.addZone (MPEZone (1, 7))); + expect (layout.addZone (MPEZone (10, 2))); + layout.clearAllZones(); + + expectEquals (layout.getNumZones(), 0); + } + + beginTest ("process MIDI buffers"); + { + MPEZoneLayout layout; + MidiBuffer buffer; + + buffer = MPEMessages::addZone (MPEZone (1, 7)); + layout.processNextMidiBuffer (buffer); + + expectEquals (layout.getNumZones(), 1); + expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1); + expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 7); + + buffer = MPEMessages::addZone (MPEZone (9, 7)); + layout.processNextMidiBuffer (buffer); + + expectEquals (layout.getNumZones(), 2); + expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1); + expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 7); + expectEquals (layout.getZoneByIndex (1)->getMasterChannel(), 9); + expectEquals (layout.getZoneByIndex (1)->getNumNoteChannels(), 7); + + MPEZone zone (1, 10); + + buffer = MPEMessages::addZone (zone); + layout.processNextMidiBuffer (buffer); + + expectEquals (layout.getNumZones(), 1); + expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1); + expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 10); + + zone.setPerNotePitchbendRange (33); + zone.setMasterPitchbendRange (44); + + buffer = MPEMessages::masterPitchbendRange (zone); + buffer.addEvents (MPEMessages::perNotePitchbendRange (zone), 0, -1, 0); + + layout.processNextMidiBuffer (buffer); + + expectEquals (layout.getZoneByIndex (0)->getPerNotePitchbendRange(), 33); + expectEquals (layout.getZoneByIndex (0)->getMasterPitchbendRange(), 44); + } + + beginTest ("process individual MIDI messages"); + { + MPEZoneLayout layout; + + layout.processNextMidiEvent (MidiMessage (0x80, 0x59, 0xd0)); // unrelated note-off msg + layout.processNextMidiEvent (MidiMessage (0xb1, 0x64, 0x06)); // RPN part 1 + layout.processNextMidiEvent (MidiMessage (0xb1, 0x65, 0x00)); // RPN part 2 + layout.processNextMidiEvent (MidiMessage (0xb8, 0x0b, 0x66)); // unrelated CC msg + layout.processNextMidiEvent (MidiMessage (0xb1, 0x06, 0x03)); // RPN part 3 + layout.processNextMidiEvent (MidiMessage (0x90, 0x60, 0x00)); // unrelated note-on msg + + expectEquals (layout.getNumZones(), 1); + expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1); + expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 3); + } + } +}; + +static MPEZoneLayoutTests MPEZoneLayoutUnitTests; + + +#endif // JUCE_UNIT_TESTS diff --git a/source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h b/source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h new file mode 100644 index 000000000..eedd6e78d --- /dev/null +++ b/source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h @@ -0,0 +1,129 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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_MPEZONELAYOUT_H_INCLUDED +#define JUCE_MPEZONELAYOUT_H_INCLUDED + + +//============================================================================== +/** + This class represents the current MPE zone layout of a device + capable of handling MPE. + + Use the MPEMessages helper class to convert the zone layout represented + by this object to MIDI message sequences that you can send to an Expressive + MIDI device to set its zone layout, add zones etc. + + @see MPEZone, MPEInstrument +*/ +class JUCE_API MPEZoneLayout +{ +public: + /** Default constructor. + + This will create a layout with no MPE zones. + You can add an MPE zone using the method addZone. + */ + MPEZoneLayout() noexcept; + + /** Adds a new MPE zone to the layout. + + @param newZone The zone to add. + + @return true if the zone was added without modifying any other zones + added previously to the same zone layout object (if any); + false if any existing MPE zones had to be truncated + or deleted entirely in order to to add this new zone. + (Note: the zone itself will always be added with the channel bounds + that were specified; this will not fail.) + */ + bool addZone (MPEZone newZone); + + /** Removes all currently present MPE zones. */ + void clearAllZones(); + + /** Pass incoming MIDI messages to an object of this class if you want the + zone layout to properly react to MPE RPN messages like an + MPE device. + MPEMessages::rpnNumber will add or remove zones; RPN 0 will + set the per-note or master pitchbend ranges. + + Any other MIDI messages will be ignored by this class. + + @see MPEMessages + */ + void processNextMidiEvent (const MidiMessage& message); + + /** Pass incoming MIDI buffers to an object of this class if you want the + zone layout to properly react to MPE RPN messages like an + MPE device. + MPEMessages::rpnNumber will add or remove zones; RPN 0 will + set the per-note or master pitchbend ranges. + + Any other MIDI messages will be ignored by this class. + + @see MPEMessages + */ + void processNextMidiBuffer (const MidiBuffer& buffer); + + /** Returns the current number of MPE zones. */ + int getNumZones() const noexcept; + + /** Returns a pointer to the MPE zone at the given index, + or nullptr if there is no such zone. + */ + MPEZone* getZoneByIndex (int index) const noexcept; + + /** Returns a pointer to the zone which uses the specified channel (1-16), + or nullptr if there is no such zone. + */ + MPEZone* getZoneByChannel (int midiChannel) const noexcept; + + /** Returns a pointer to the zone which has the specified channel (1-16) + as its master channel, or nullptr if there is no such zone. + */ + MPEZone* getZoneByMasterChannel (int midiChannel) const noexcept; + + /** Returns a pointer to the zone which has the specified channel (1-16) + as its first note channel, or nullptr if there is no such zone. + */ + MPEZone* getZoneByFirstNoteChannel (int midiChannel) const noexcept; + + /** Returns a pointer to the zone which has the specified channel (1-16) + as one of its note channels, or nullptr if there is no such zone. + */ + MPEZone* getZoneByNoteChannel (int midiChannel) const noexcept; + +private: + //========================================================================== + Array zones; + MidiRPNDetector rpnDetector; + + void processRpnMessage (MidiRPNMessage); + void processZoneLayoutRpnMessage (MidiRPNMessage); + void processPitchbendRangeRpnMessage (MidiRPNMessage); +}; + + +#endif // JUCE_MPEZONELAYOUT_H_INCLUDED diff --git a/source/modules/juce_audio_basics/sources/juce_PositionableAudioSource.h b/source/modules/juce_audio_basics/sources/juce_PositionableAudioSource.h index 2084f0be9..f69e6d569 100644 --- a/source/modules/juce_audio_basics/sources/juce_PositionableAudioSource.h +++ b/source/modules/juce_audio_basics/sources/juce_PositionableAudioSource.h @@ -71,7 +71,7 @@ public: 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; } + virtual void setLooping (bool shouldLoop) { ignoreUnused (shouldLoop); } }; diff --git a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp index 2aa10fb7c..7f27b33d7 100644 --- a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp +++ b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp @@ -114,6 +114,7 @@ void Synthesiser::clearVoices() SynthesiserVoice* Synthesiser::addVoice (SynthesiserVoice* const newVoice) { const ScopedLock sl (lock); + newVoice->setCurrentPlaybackSampleRate (sampleRate); return voices.add (newVoice); } @@ -511,13 +512,13 @@ void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown) void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/) { - (void) midiChannel; + ignoreUnused (midiChannel); jassert (midiChannel > 0 && midiChannel <= 16); } void Synthesiser::handleProgramChange (int midiChannel, int programNumber) { - (void) midiChannel; (void) programNumber; + ignoreUnused (midiChannel, programNumber); jassert (midiChannel > 0 && midiChannel <= 16); } @@ -557,6 +558,9 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, // - Re-use the oldest notes first // - Protect the lowest & topmost notes, even if sustained, but not if they've been released. + // apparently you are trying to render audio without having any voices... + jassert (voices.size() > 0); + // These are the voices we want to protect (ie: only steal if unavoidable) SynthesiserVoice* low = nullptr; // Lowest sounding note, might be sustained, but NOT in release phase SynthesiserVoice* top = nullptr; // Highest sounding note, might be sustained, but NOT in release phase diff --git a/source/modules/juce_audio_devices.h b/source/modules/juce_audio_devices.h deleted file mode 100644 index f3b4051f6..000000000 --- a/source/modules/juce_audio_devices.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Carla Juce setup - * Copyright (C) 2013-2014 Filipe Coelho - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or any later version. - * - * This program 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. - * - * For a full copy of the GNU General Public License see the doc/GPL.txt file. - */ - -#ifndef CARLA_JUCE_AUDIO_DEVICES_H_INCLUDED -#define CARLA_JUCE_AUDIO_DEVICES_H_INCLUDED - -#include "juce_events.h" -#include "juce_audio_basics.h" -#include "juce_audio_formats.h" - -#if 1 //JUCE_MAC || JUCE_WINDOWS -# include "juce_audio_devices/AppConfig.h" -# include "juce_audio_devices/juce_audio_devices.h" -#endif - -#endif // CARLA_JUCE_AUDIO_DEVICES_H_INCLUDED diff --git a/source/modules/juce_audio_devices/AppConfig.h b/source/modules/juce_audio_devices/AppConfig.h deleted file mode 100755 index f4cf16b16..000000000 --- a/source/modules/juce_audio_devices/AppConfig.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - ============================================================================== - - Build options for juce_audio_devices static library - - ============================================================================== -*/ - -#ifndef CARLA_JUCE_AUDIO_DEVICES_APPCONFIG_H_INCLUDED -#define CARLA_JUCE_AUDIO_DEVICES_APPCONFIG_H_INCLUDED - -#include "../juce_events/AppConfig.h" -#include "../juce_audio_basics/AppConfig.h" -#include "../juce_audio_formats/AppConfig.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. -*/ -#if JUCE_WINDOWS - #define JUCE_ASIO 1 -#else - #define JUCE_ASIO 0 -#endif - -/** Config: JUCE_WASAPI - Enables WASAPI audio devices (Windows Vista and above). -*/ -#define JUCE_WASAPI 0 - -/** Config: JUCE_DIRECTSOUND - Enables DirectSound audio (MS Windows only). -*/ -#if JUCE_WINDOWS - #define JUCE_DIRECTSOUND 1 -#else - #define JUCE_DIRECTSOUND 0 -#endif - -/** Config: JUCE_ALSA - Enables ALSA audio devices (Linux only). -*/ -#if 0 //JUCE_LINUX - #define JUCE_ALSA 1 - #define JUCE_ALSA_MIDI_INPUT_NAME "Carla" - #define JUCE_ALSA_MIDI_OUTPUT_NAME "Carla" - #define JUCE_ALSA_MIDI_INPUT_PORT_NAME "Midi In" - #define JUCE_ALSA_MIDI_OUTPUT_PORT_NAME "Midi Out" -#else - #define JUCE_ALSA 0 -#endif - -/** Config: JUCE_JACK - Enables JACK audio devices (Linux only). -*/ -#if 0 //JUCE_LINUX - #define JUCE_JACK 1 - #define JUCE_JACK_CLIENT_NAME "Carla" -#else - #define JUCE_JACK 0 -#endif - -//============================================================================= -/** Config: JUCE_USE_CDREADER - Enables the AudioCDReader class (on supported platforms). -*/ -#define JUCE_USE_CDREADER 0 - -/** Config: JUCE_USE_CDBURNER - Enables the AudioCDBurner class (on supported platforms). -*/ -#define JUCE_USE_CDBURNER 0 - -#endif // CARLA_JUCE_AUDIO_DEVICES_APPCONFIG_H_INCLUDED diff --git a/source/modules/juce_audio_devices/Makefile b/source/modules/juce_audio_devices/Makefile index f6631b41d..d4d41e751 100644 --- a/source/modules/juce_audio_devices/Makefile +++ b/source/modules/juce_audio_devices/Makefile @@ -10,7 +10,7 @@ include ../Makefile.mk # ---------------------------------------------------------------------------------------------------------------------------- -BUILD_CXX_FLAGS += $(JUCE_AUDIO_DEVICES_FLAGS) -w +BUILD_CXX_FLAGS += $(JUCE_AUDIO_DEVICES_FLAGS) -I.. ifeq ($(WIN32),true) # BUILD_CXX_FLAGS += -I$(CWD)/includes/asio diff --git a/source/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.h b/source/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.h index bcf0f4808..bb9ac6ec8 100644 --- a/source/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.h +++ b/source/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.h @@ -151,7 +151,7 @@ private: File volumeDir; Array tracks; int currentReaderTrack; - ScopedPointer reader; + ScopedPointer reader; AudioCDReader (const File& volume); #elif JUCE_WINDOWS diff --git a/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp b/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp index ffd068a95..4dc948bec 100644 --- a/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp +++ b/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp @@ -86,6 +86,65 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackHandler) }; +//============================================================================== +// This is an AudioTransportSource which will own it's assigned source +struct AudioSourceOwningTransportSource : public AudioTransportSource +{ + AudioSourceOwningTransportSource (PositionableAudioSource* s) : source (s) + { + AudioTransportSource::setSource (s); + } + + ~AudioSourceOwningTransportSource() + { + setSource (nullptr); + } + +private: + ScopedPointer source; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSourceOwningTransportSource) +}; + +//============================================================================== +// An AudioSourcePlayer which will remove itself from the AudioDeviceManager's +// callback list once it finishes playing its source +struct AutoRemovingSourcePlayer : public AudioSourcePlayer, + private Timer +{ + AutoRemovingSourcePlayer (AudioDeviceManager& dm, AudioTransportSource* ts, bool ownSource) + : manager (dm), transportSource (ts, ownSource) + { + jassert (ts != nullptr); + manager.addAudioCallback (this); + AudioSourcePlayer::setSource (transportSource); + startTimerHz (10); + } + + ~AutoRemovingSourcePlayer() + { + setSource (nullptr); + manager.removeAudioCallback (this); + } + + void timerCallback() override + { + if (getCurrentSource() == nullptr || ! transportSource->isPlaying()) + delete this; + } + + void audioDeviceStopped() override + { + AudioSourcePlayer::audioDeviceStopped(); + setSource (nullptr); + } + +private: + AudioDeviceManager& manager; + OptionalScopedPointer transportSource; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AutoRemovingSourcePlayer) +}; //============================================================================== AudioDeviceManager::AudioDeviceManager() @@ -103,8 +162,11 @@ AudioDeviceManager::~AudioDeviceManager() { currentAudioDevice = nullptr; defaultMidiOutput = nullptr; -} + for (int i = 0; i < callbacks.size(); ++i) + if (AutoRemovingSourcePlayer* p = dynamic_cast (callbacks.getUnchecked(i))) + delete p; +} //============================================================================== void AudioDeviceManager::createDeviceTypesIfNeeded() @@ -926,143 +988,16 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName) } } -//============================================================================== -// This is an AudioTransportSource which will own it's assigned source -class AudioSourceOwningTransportSource : public AudioTransportSource -{ -public: - AudioSourceOwningTransportSource() {} - ~AudioSourceOwningTransportSource() { setSource (nullptr); } - - void setSource (PositionableAudioSource* newSource) - { - if (src != newSource) - { - ScopedPointer oldSourceDeleter (src); - src = newSource; - - // tell the base class about the new source before deleting the old one - AudioTransportSource::setSource (newSource); - } - } - -private: - ScopedPointer src; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSourceOwningTransportSource) -}; - -//============================================================================== -// An Audio player which will remove itself from the AudioDeviceManager's -// callback list once it finishes playing its source -class AutoRemovingSourcePlayer : public AudioSourcePlayer, - private ChangeListener -{ -public: - struct DeleteOnMessageThread : public CallbackMessage - { - DeleteOnMessageThread (AutoRemovingSourcePlayer* p) : parent (p) {} - void messageCallback() override { delete parent; } - - AutoRemovingSourcePlayer* parent; - }; - - //============================================================================== - AutoRemovingSourcePlayer (AudioDeviceManager& deviceManager, bool ownSource) - : manager (deviceManager), - deleteWhenDone (ownSource), - hasAddedCallback (false), - recursiveEntry (false) - { - } - - void changeListenerCallback (ChangeBroadcaster* newSource) override - { - if (AudioTransportSource* currentTransport - = dynamic_cast (getCurrentSource())) - { - ignoreUnused (newSource); - jassert (newSource == currentTransport); - - if (! currentTransport->isPlaying()) - { - // this will call audioDeviceStopped! - manager.removeAudioCallback (this); - } - else if (! hasAddedCallback) - { - hasAddedCallback = true; - manager.addAudioCallback (this); - } - } - } - - void audioDeviceStopped() override - { - if (! recursiveEntry) - { - ScopedValueSetter s (recursiveEntry, true, false); - - manager.removeAudioCallback (this); - AudioSourcePlayer::audioDeviceStopped(); - - if (MessageManager* mm = MessageManager::getInstanceWithoutCreating()) - { - if (mm->isThisTheMessageThread()) - delete this; - else - (new DeleteOnMessageThread (this))->post(); - } - } - } - - void setSource (AudioTransportSource* newSource) - { - AudioSource* oldSource = getCurrentSource(); - - if (AudioTransportSource* oldTransport = dynamic_cast (oldSource)) - oldTransport->removeChangeListener (this); - - if (newSource != nullptr) - newSource->addChangeListener (this); - - AudioSourcePlayer::setSource (newSource); - - if (deleteWhenDone) - delete oldSource; - } - -private: - // only allow myself to be deleted when my audio callback has been removed - ~AutoRemovingSourcePlayer() - { - setSource (nullptr); - } - - AudioDeviceManager& manager; - bool deleteWhenDone, hasAddedCallback, recursiveEntry; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AutoRemovingSourcePlayer) -}; - //============================================================================== // An AudioSource which simply outputs a buffer -class AudioSampleBufferSource : public PositionableAudioSource +class AudioSampleBufferSource : public PositionableAudioSource { public: - AudioSampleBufferSource (AudioSampleBuffer* audioBuffer, bool shouldLoop, bool ownBuffer) - : position (0), - buffer (audioBuffer), - looping (shouldLoop), - deleteWhenDone (ownBuffer) + AudioSampleBufferSource (AudioSampleBuffer* audioBuffer, bool ownBuffer) + : buffer (audioBuffer, ownBuffer), + position (0), looping (false) {} - ~AudioSampleBufferSource() - { - if (deleteWhenDone) - delete buffer; - } - //============================================================================== void setNextReadPosition (int64 newPosition) override { @@ -1074,69 +1009,45 @@ public: position = jmin (buffer->getNumSamples(), static_cast (newPosition)); } - int64 getNextReadPosition() const override - { - return static_cast (position); - } - - int64 getTotalLength() const override - { - return static_cast (buffer->getNumSamples()); - } - - bool isLooping() const override - { - return looping; - } + int64 getNextReadPosition() const override { return static_cast (position); } + int64 getTotalLength() const override { return static_cast (buffer->getNumSamples()); } - void setLooping (bool shouldLoop) override - { - looping = shouldLoop; - } + bool isLooping() const override { return looping; } + void setLooping (bool shouldLoop) override { looping = shouldLoop; } //============================================================================== - void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override - { - ignoreUnused (samplesPerBlockExpected, sampleRate); - } - - void releaseResources() override - {} + void prepareToPlay (int, double) override {} + void releaseResources() override {} void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override { - int max = jmin (buffer->getNumSamples() - position, bufferToFill.numSamples); - - jassert (max >= 0); - { - int ch; - int maxInChannels = buffer->getNumChannels(); - int maxOutChannels = jmin (bufferToFill.buffer->getNumChannels(), - jmax (maxInChannels, 2)); + bufferToFill.clearActiveBufferRegion(); - for (ch = 0; ch < maxOutChannels; ch++) - { - int inChannel = ch % maxInChannels; + const int bufferSize = buffer->getNumSamples(); + const int samplesNeeded = bufferToFill.numSamples; + const int samplesToCopy = jmin (bufferSize - position, samplesNeeded); - if (max > 0) - bufferToFill.buffer->copyFrom (ch, bufferToFill.startSample, *buffer, inChannel, position, max); - } + if (samplesToCopy > 0) + { + const int maxInChannels = buffer->getNumChannels(); + const int maxOutChannels = jmin (bufferToFill.buffer->getNumChannels(), jmax (maxInChannels, 2)); - for (; ch < bufferToFill.buffer->getNumChannels(); ++ch) - bufferToFill.buffer->clear (ch, bufferToFill.startSample, bufferToFill.numSamples); + for (int i = 0; i < maxOutChannels; ++i) + bufferToFill.buffer->copyFrom (i, bufferToFill.startSample, *buffer, + i % maxInChannels, position, samplesToCopy); } - position += max; + position += samplesNeeded; if (looping) - position = position % buffer->getNumSamples(); + position %= bufferSize; } private: //============================================================================== + OptionalScopedPointer buffer; int position; - AudioSampleBuffer* buffer; - bool looping, deleteWhenDone; + bool looping; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSampleBufferSource) }; @@ -1165,38 +1076,38 @@ void AudioDeviceManager::playSound (const void* resourceData, size_t resourceSiz void AudioDeviceManager::playSound (AudioFormatReader* reader, bool deleteWhenFinished) { - playSound (new AudioFormatReaderSource (reader, deleteWhenFinished), true); + if (reader != nullptr) + playSound (new AudioFormatReaderSource (reader, deleteWhenFinished), true); +} + +void AudioDeviceManager::playSound (AudioSampleBuffer* buffer, bool deleteWhenFinished) +{ + if (buffer != nullptr) + playSound (new AudioSampleBufferSource (buffer, deleteWhenFinished), true); } void AudioDeviceManager::playSound (PositionableAudioSource* audioSource, bool deleteWhenFinished) { if (audioSource != nullptr && currentAudioDevice != nullptr) { - if (AudioTransportSource* transport = dynamic_cast (audioSource)) - { - AutoRemovingSourcePlayer* player = new AutoRemovingSourcePlayer (*this, deleteWhenFinished); - player->setSource (transport); - } - else - { - AudioTransportSource* transportSource; + AudioTransportSource* transport = dynamic_cast (audioSource); + if (transport == nullptr) + { if (deleteWhenFinished) { - AudioSourceOwningTransportSource* owningTransportSource = new AudioSourceOwningTransportSource(); - owningTransportSource->setSource (audioSource); - transportSource = owningTransportSource; + transport = new AudioSourceOwningTransportSource (audioSource); } else { - transportSource = new AudioTransportSource; - transportSource->setSource (audioSource); + transport = new AudioTransportSource(); + transport->setSource (audioSource); + deleteWhenFinished = true; } - - // recursively call myself - playSound (transportSource, true); - transportSource->start(); } + + transport->start(); + new AutoRemovingSourcePlayer (*this, transport, deleteWhenFinished); } else { @@ -1205,11 +1116,6 @@ void AudioDeviceManager::playSound (PositionableAudioSource* audioSource, bool d } } -void AudioDeviceManager::playSound (AudioSampleBuffer* buffer, bool deleteWhenFinished) -{ - playSound (new AudioSampleBufferSource (buffer, false, deleteWhenFinished), true); -} - void AudioDeviceManager::playTestSound() { const double sampleRate = currentAudioDevice->getCurrentSampleRate(); diff --git a/source/modules/juce_audio_devices/juce_audio_devices.cpp b/source/modules/juce_audio_devices/juce_audio_devices.cpp index 2bc3c6a09..172b0e557 100644 --- a/source/modules/juce_audio_devices/juce_audio_devices.cpp +++ b/source/modules/juce_audio_devices/juce_audio_devices.cpp @@ -22,7 +22,7 @@ ============================================================================== */ -#if defined (JUCE_AUDIO_DEVICES_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE +#ifdef JUCE_AUDIO_DEVICES_H_INCLUDED /* 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 @@ -31,10 +31,6 @@ #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" diff --git a/source/modules/juce_audio_devices/midi_io/juce_MidiInput.h b/source/modules/juce_audio_devices/midi_io/juce_MidiInput.h index 0c5a5b62e..65c51a75e 100644 --- a/source/modules/juce_audio_devices/midi_io/juce_MidiInput.h +++ b/source/modules/juce_audio_devices/midi_io/juce_MidiInput.h @@ -74,8 +74,7 @@ public: int numBytesSoFar, double timestamp) { - // (this bit is just to avoid compiler warnings about unused variables) - (void) source; (void) messageData; (void) numBytesSoFar; (void) timestamp; + ignoreUnused (source, messageData, numBytesSoFar, timestamp); } }; diff --git a/source/modules/juce_audio_devices/midi_io/juce_MidiOutput.cpp b/source/modules/juce_audio_devices/midi_io/juce_MidiOutput.cpp index 4881afbdd..e09ed0825 100644 --- a/source/modules/juce_audio_devices/midi_io/juce_MidiOutput.cpp +++ b/source/modules/juce_audio_devices/midi_io/juce_MidiOutput.cpp @@ -40,6 +40,16 @@ MidiOutput::MidiOutput(const String& midiName) { } +void MidiOutput::sendBlockOfMessagesNow (const MidiBuffer& buffer) +{ + MidiBuffer::Iterator i (buffer); + MidiMessage message; + int samplePosition; // Note: not actually used, so no need to initialise. + + while (i.getNextEvent (message, samplePosition)) + sendMessageNow (message); +} + void MidiOutput::sendBlockOfMessages (const MidiBuffer& buffer, const double millisecondCounterToStartAt, double samplesPerSecondForBuffer) diff --git a/source/modules/juce_audio_devices/midi_io/juce_MidiOutput.h b/source/modules/juce_audio_devices/midi_io/juce_MidiOutput.h index d22926992..b74447773 100644 --- a/source/modules/juce_audio_devices/midi_io/juce_MidiOutput.h +++ b/source/modules/juce_audio_devices/midi_io/juce_MidiOutput.h @@ -87,11 +87,12 @@ public: /** Returns the name of this device. */ const String& getName() const noexcept { return name; } - /** Makes this device output a midi message. - @see MidiMessage - */ + /** Sends out a MIDI message immediately. */ void sendMessageNow (const MidiMessage& message); + /** Sends out a sequence of MIDI messages immediately. */ + void sendBlockOfMessagesNow (const MidiBuffer& buffer); + //============================================================================== /** This lets you supply a block of messages that will be sent out at some point in the future. diff --git a/source/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h b/source/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h index 0cefb83b1..62eb5346b 100644 --- a/source/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h +++ b/source/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h @@ -51,7 +51,7 @@ public: void pushMidiData (const void* inputData, int numBytes, double time, UserDataType* input, CallbackType& callback) { - const uint8* d = static_cast (inputData); + const uint8* d = static_cast (inputData); while (numBytes > 0) { diff --git a/source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp b/source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp index e2e6f8340..08580aa08 100644 --- a/source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp +++ b/source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp @@ -22,6 +22,8 @@ ============================================================================== */ +#undef check + const char* const openSLTypeName = "Android OpenSL"; bool isOpenSLAvailable() @@ -43,7 +45,7 @@ public: { // 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); + AndroidAudioIODevice javaDevice (deviceName); // this is a total guess about how to calculate the latency, but seems to vaguely agree // with the devices I've tested.. YMMV @@ -550,7 +552,7 @@ private: static void staticCallback (SLAndroidSimpleBufferQueueItf queue, void* context) noexcept { - jassert (queue == static_cast (context)->playerBufferQueue); (void) queue; + jassert (queue == static_cast (context)->playerBufferQueue); ignoreUnused (queue); static_cast (context)->bufferList.bufferReturned(); } @@ -685,7 +687,7 @@ private: static void staticCallback (SLAndroidSimpleBufferQueueItf queue, void* context) noexcept { - jassert (queue == static_cast (context)->recorderBufferQueue); (void) queue; + jassert (queue == static_cast (context)->recorderBufferQueue); ignoreUnused (queue); static_cast (context)->bufferList.bufferReturned(); } diff --git a/source/modules/juce_audio_devices/native/juce_ios_Audio.cpp b/source/modules/juce_audio_devices/native/juce_ios_Audio.cpp index 16dba0c36..53a319def 100644 --- a/source/modules/juce_audio_devices/native/juce_ios_Audio.cpp +++ b/source/modules/juce_audio_devices/native/juce_ios_Audio.cpp @@ -393,13 +393,13 @@ private: static void interruptionListenerCallback (void* client, UInt32 interruptionType) { - const Array & activeDevices = static_cast (client)->activeDevices; + const Array& activeDevices = static_cast (client)->activeDevices; for (int i = activeDevices.size(); --i >= 0;) activeDevices.getUnchecked(i)->interruptionListener (interruptionType); } - Array activeDevices; + Array activeDevices; }; static AudioSessionHolder& getSessionHolder() diff --git a/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp b/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp index 85fb8c514..3fb5505a1 100644 --- a/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp +++ b/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp @@ -993,7 +993,7 @@ public: { jassert (hasScanned); // need to call scanForDevices() before doing this - if (ALSAAudioIODevice* d = dynamic_cast (device)) + if (ALSAAudioIODevice* d = dynamic_cast (device)) return asInput ? inputIds.indexOf (d->inputId) : outputIds.indexOf (d->outputId); diff --git a/source/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp b/source/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp index ac9846f13..d9dd86462 100644 --- a/source/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp +++ b/source/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp @@ -388,7 +388,7 @@ private: if (callback != nullptr) { if ((numActiveInChans + numActiveOutChans) > 0) - callback->audioDeviceIOCallback (const_cast (inChans.getData()), numActiveInChans, + callback->audioDeviceIOCallback (const_cast (inChans.getData()), numActiveInChans, outChans, numActiveOutChans, numSamples); } else @@ -437,7 +437,7 @@ private: static void portConnectCallback (jack_port_id_t, jack_port_id_t, int, void* arg) { - if (JackAudioIODevice* device = static_cast (arg)) + if (JackAudioIODevice* device = static_cast (arg)) device->updateActivePorts(); } @@ -470,7 +470,7 @@ private: AudioIODeviceCallback* callback; CriticalSection callbackLock; - HeapBlock inChans, outChans; + HeapBlock inChans, outChans; int totalNumberOfInputChannels; int totalNumberOfOutputChannels; Array inputPorts, outputPorts; @@ -557,7 +557,7 @@ public: { jassert (hasScanned); // need to call scanForDevices() before doing this - if (JackAudioIODevice* d = dynamic_cast (device)) + if (JackAudioIODevice* d = dynamic_cast (device)) return asInput ? inputIds.indexOf (d->inputId) : outputIds.indexOf (d->outputId); diff --git a/source/modules/juce_audio_devices/native/juce_linux_Midi.cpp b/source/modules/juce_audio_devices/native/juce_linux_Midi.cpp index 7801a286e..f042c8559 100644 --- a/source/modules/juce_audio_devices/native/juce_linux_Midi.cpp +++ b/source/modules/juce_audio_devices/native/juce_linux_Midi.cpp @@ -136,7 +136,7 @@ private: HeapBlock pfd ((size_t) numPfds); snd_seq_poll_descriptors (seqHandle, pfd, (unsigned int) numPfds, POLLIN); - HeapBlock buffer (maxEventSize); + HeapBlock buffer (maxEventSize); while (! threadShouldExit()) { diff --git a/source/modules/juce_audio_devices/native/juce_mac_AudioCDReader.mm b/source/modules/juce_audio_devices/native/juce_mac_AudioCDReader.mm index 8d87fac11..1ef5bd3ec 100644 --- a/source/modules/juce_audio_devices/native/juce_mac_AudioCDReader.mm +++ b/source/modules/juce_audio_devices/native/juce_mac_AudioCDReader.mm @@ -169,7 +169,7 @@ void AudioCDReader::refreshTrackLengths() { XmlDocument doc (toc); const char* error = CDReaderHelpers::getTrackOffsets (doc, trackStartSamples); - (void) error; // could be logged.. + ignoreUnused (error); // could be logged.. lengthInSamples = trackStartSamples.getLast() - trackStartSamples.getFirst(); } diff --git a/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp b/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp index 31af32b6e..b5768f40c 100644 --- a/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp +++ b/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp @@ -155,9 +155,7 @@ public: outputLatency (0), bitDepth (32), callback (nullptr), - #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 audioProcID (0), - #endif deviceID (id), started (false), sampleRate (0), @@ -625,11 +623,7 @@ public: if (deviceID != 0) { - #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 - if (OK (AudioDeviceAddIOProc (deviceID, audioIOProc, this))) - #else if (OK (AudioDeviceCreateIOProcID (deviceID, audioIOProc, this, &audioProcID))) - #endif { if (OK (AudioDeviceStart (deviceID, audioIOProc))) { @@ -637,12 +631,8 @@ public: } else { - #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 - OK (AudioDeviceRemoveIOProc (deviceID, audioIOProc)); - #else OK (AudioDeviceDestroyIOProcID (deviceID, audioProcID)); audioProcID = 0; - #endif } } } @@ -669,13 +659,8 @@ public: && ! leaveInterruptRunning) { OK (AudioDeviceStop (deviceID, audioIOProc)); - - #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 - OK (AudioDeviceRemoveIOProc (deviceID, audioIOProc)); - #else OK (AudioDeviceDestroyIOProcID (deviceID, audioProcID)); audioProcID = 0; - #endif started = false; @@ -793,9 +778,7 @@ public: Array sampleRates; Array bufferSizes; AudioIODeviceCallback* callback; - #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 AudioDeviceIOProcID audioProcID; - #endif private: CriticalSection callbackLock; diff --git a/source/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp b/source/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp index d1ad9370e..a5d75b779 100644 --- a/source/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp +++ b/source/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp @@ -37,7 +37,7 @@ namespace CoreMidiHelpers Logger::writeToLog ("CoreMIDI error: " + String (lineNum) + " - " + String::toHexString ((int) err)); #endif - (void) lineNum; + ignoreUnused (lineNum); return false; } diff --git a/source/modules/juce_audio_devices/native/juce_win32_ASIO.cpp b/source/modules/juce_audio_devices/native/juce_win32_ASIO.cpp index e6d802a98..16c8b524b 100644 --- a/source/modules/juce_audio_devices/native/juce_win32_ASIO.cpp +++ b/source/modules/juce_audio_devices/native/juce_win32_ASIO.cpp @@ -67,7 +67,7 @@ namespace ASIODebugging #else static void dummyLog() {} #define JUCE_ASIO_LOG(msg) ASIODebugging::dummyLog() - #define JUCE_ASIO_LOG_ERROR(msg, errNum) (void) errNum; ASIODebugging::dummyLog() + #define JUCE_ASIO_LOG_ERROR(msg, errNum) ignoreUnused (errNum); ASIODebugging::dummyLog() #endif } @@ -690,31 +690,24 @@ public: JUCE_ASIO_LOG ("showing control panel"); bool done = false; + insideControlPanelModalLoop = true; - JUCE_TRY - { - // are there are devices that need to be closed before showing their control panel? - // close(); - insideControlPanelModalLoop = true; - - const uint32 started = Time::getMillisecondCounter(); + const uint32 started = Time::getMillisecondCounter(); - if (asioObject != nullptr) - { - asioObject->controlPanel(); + if (asioObject != nullptr) + { + asioObject->controlPanel(); - const int spent = (int) Time::getMillisecondCounter() - (int) started; + const int spent = (int) Time::getMillisecondCounter() - (int) started; - JUCE_ASIO_LOG ("spent: " + String (spent)); + JUCE_ASIO_LOG ("spent: " + String (spent)); - if (spent > 300) - { - shouldUsePreferredSize = true; - done = true; - } + if (spent > 300) + { + shouldUsePreferredSize = true; + done = true; } } - JUCE_CATCH_ALL insideControlPanelModalLoop = false; return done; @@ -785,7 +778,7 @@ private: HeapBlock inputFormat, outputFormat; WaitableEvent event1; - HeapBlock tempBuffer; + HeapBlock tempBuffer; int volatile bufferIndex, numActiveInputChans, numActiveOutputChans; bool deviceIsOpen, isStarted, buffersCreated; diff --git a/source/modules/juce_audio_devices/native/juce_win32_AudioCDBurner.cpp b/source/modules/juce_audio_devices/native/juce_win32_AudioCDBurner.cpp index 03a3c326b..997484f2f 100644 --- a/source/modules/juce_audio_devices/native/juce_win32_AudioCDBurner.cpp +++ b/source/modules/juce_audio_devices/native/juce_win32_AudioCDBurner.cpp @@ -367,7 +367,7 @@ bool AudioCDBurner::addAudioTrack (AudioSource* audioSource, int numSamples) hr = pimpl->redbook->CreateAudioTrack ((long) numSamples / (bytesPerBlock * 4)); - HeapBlock buffer (bytesPerBlock); + HeapBlock buffer (bytesPerBlock); AudioSampleBuffer sourceBuffer (2, samplesPerBlock); int samplesDone = 0; diff --git a/source/modules/juce_audio_devices/native/juce_win32_AudioCDReader.cpp b/source/modules/juce_audio_devices/native/juce_win32_AudioCDReader.cpp index fe9076a7a..ea99f80ab 100644 --- a/source/modules/juce_audio_devices/native/juce_win32_AudioCDReader.cpp +++ b/source/modules/juce_audio_devices/native/juce_win32_AudioCDReader.cpp @@ -1027,7 +1027,7 @@ AudioCDReader::AudioCDReader (void* handle_) AudioCDReader::~AudioCDReader() { using namespace CDReaderHelpers; - CDDeviceWrapper* const device = static_cast (handle); + CDDeviceWrapper* const device = static_cast (handle); delete device; } @@ -1035,7 +1035,7 @@ bool AudioCDReader::readSamples (int** destSamples, int numDestChannels, int sta int64 startSampleInFile, int numSamples) { using namespace CDReaderHelpers; - CDDeviceWrapper* const device = static_cast (handle); + CDDeviceWrapper* const device = static_cast (handle); bool ok = true; @@ -1131,7 +1131,7 @@ bool AudioCDReader::isCDStillPresent() const { using namespace CDReaderHelpers; TOC toc = { 0 }; - return static_cast (handle)->deviceHandle.readTOC (&toc); + return static_cast (handle)->deviceHandle.readTOC (&toc); } void AudioCDReader::refreshTrackLengths() @@ -1142,7 +1142,7 @@ void AudioCDReader::refreshTrackLengths() TOC toc = { 0 }; - if (static_cast (handle)->deviceHandle.readTOC (&toc)) + if (static_cast (handle)->deviceHandle.readTOC (&toc)) { int numTracks = 1 + toc.lastTrack - toc.firstTrack; @@ -1174,7 +1174,7 @@ int AudioCDReader::getLastIndex() const int AudioCDReader::getIndexAt (int samplePos) { using namespace CDReaderHelpers; - CDDeviceWrapper* const device = static_cast (handle); + CDDeviceWrapper* const device = static_cast (handle); const int frameNeeded = samplePos / samplesPerFrame; @@ -1255,7 +1255,7 @@ Array AudioCDReader::findIndexesInTrack (const int trackNumber) if (needToScan) { - CDDeviceWrapper* const device = static_cast (handle); + CDDeviceWrapper* const device = static_cast (handle); int pos = trackStart; int last = -1; @@ -1305,5 +1305,5 @@ Array AudioCDReader::findIndexesInTrack (const int trackNumber) void AudioCDReader::ejectDisk() { using namespace CDReaderHelpers; - static_cast (handle)->deviceHandle.openDrawer (true); + static_cast (handle)->deviceHandle.openDrawer (true); } diff --git a/source/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp b/source/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp index 1491ab290..410e98062 100644 --- a/source/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp +++ b/source/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp @@ -182,11 +182,9 @@ namespace DSoundLogging } } - #define CATCH JUCE_CATCH_EXCEPTION #define JUCE_DS_LOG(a) DSoundLogging::logMessage(a); #define JUCE_DS_LOG_ERROR(a) DSoundLogging::logError(a, __LINE__); #else - #define CATCH JUCE_CATCH_ALL #define JUCE_DS_LOG(a) #define JUCE_DS_LOG_ERROR(a) #endif @@ -251,7 +249,7 @@ public: { JUCE_DS_LOG ("closing output: " + name); HRESULT hr = pOutputBuffer->Stop(); - JUCE_DS_LOG_ERROR (hr); (void) hr; + JUCE_DS_LOG_ERROR (hr); ignoreUnused (hr); pOutputBuffer->Release(); pOutputBuffer = nullptr; @@ -354,7 +352,7 @@ public: hr = pOutputBuffer->Play (0, 0, 1 /* DSBPLAY_LOOPING */); if (SUCCEEDED (hr)) - return String::empty; + return String(); } } } @@ -534,7 +532,7 @@ public: { JUCE_DS_LOG ("closing input: " + name); HRESULT hr = pInputBuffer->Stop(); - JUCE_DS_LOG_ERROR (hr); (void) hr; + JUCE_DS_LOG_ERROR (hr); ignoreUnused (hr); pInputBuffer->Release(); pInputBuffer = nullptr; @@ -597,7 +595,7 @@ public: hr = pInputBuffer->Start (1 /* DSCBSTART_LOOPING */); if (SUCCEEDED (hr)) - return String::empty; + return String(); } } @@ -1226,7 +1224,7 @@ public: { jassert (hasScanned); // need to call scanForDevices() before doing this - if (DSoundAudioIODevice* const d = dynamic_cast (device)) + if (DSoundAudioIODevice* const d = dynamic_cast (device)) return asInput ? d->inputDeviceIndex : d->outputDeviceIndex; diff --git a/source/modules/juce_audio_devices/native/juce_win32_Midi.cpp b/source/modules/juce_audio_devices/native/juce_win32_Midi.cpp index 042c98d01..97e4b722f 100644 --- a/source/modules/juce_audio_devices/native/juce_win32_Midi.cpp +++ b/source/modules/juce_audio_devices/native/juce_win32_Midi.cpp @@ -116,7 +116,7 @@ public: static void CALLBACK midiInCallback (HMIDIIN, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR midiMessage, DWORD_PTR timeStamp) { - MidiInCollector* const collector = reinterpret_cast (dwInstance); + MidiInCollector* const collector = reinterpret_cast (dwInstance); if (activeMidiCollectors.contains (collector)) { @@ -270,8 +270,8 @@ MidiInput* MidiInput::openDevice (const int index, MidiInputCallback* const call } } - ScopedPointer in (new MidiInput (name)); - ScopedPointer collector (new MidiInCollector (in, *callback)); + ScopedPointer in (new MidiInput (name)); + ScopedPointer collector (new MidiInCollector (in, *callback)); HMIDIIN h; MMRESULT err = midiInOpen (&h, deviceId, diff --git a/source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp b/source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp index 1b2a4ad49..8211dbc2e 100644 --- a/source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp +++ b/source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp @@ -32,7 +32,7 @@ namespace WasapiClasses void logFailure (HRESULT hr) { - (void) hr; + ignoreUnused (hr); jassert (hr != (HRESULT) 0x800401f0); // If you hit this, it means you're trying to call from // a thread which hasn't been initialised with CoInitialize(). diff --git a/source/modules/juce_audio_formats.h b/source/modules/juce_audio_formats.h deleted file mode 100644 index 168caf205..000000000 --- a/source/modules/juce_audio_formats.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Carla Juce setup - * Copyright (C) 2013-2014 Filipe Coelho - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or any later version. - * - * This program 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. - * - * For a full copy of the GNU General Public License see the doc/GPL.txt file. - */ - -#ifndef CARLA_JUCE_AUDIO_FORMATS_H_INCLUDED -#define CARLA_JUCE_AUDIO_FORMATS_H_INCLUDED - -#include "juce_audio_basics.h" - -#include "juce_audio_formats/AppConfig.h" -#include "juce_audio_formats/juce_audio_formats.h" - -#endif // CARLA_JUCE_AUDIO_FORMATS_H_INCLUDED diff --git a/source/modules/juce_audio_formats/AppConfig.h b/source/modules/juce_audio_formats/AppConfig.h deleted file mode 100755 index f68cf42af..000000000 --- a/source/modules/juce_audio_formats/AppConfig.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - ============================================================================== - - Build options for juce_audio_formats static library - - ============================================================================== -*/ - -#ifndef CARLA_JUCE_AUDIO_FORMATS_APPCONFIG_H_INCLUDED -#define CARLA_JUCE_AUDIO_FORMATS_APPCONFIG_H_INCLUDED - -#include "../juce_audio_basics/AppConfig.h" - -//============================================================================= -/** Config: JUCE_USE_FLAC - Enables the FLAC audio codec classes (available on all platforms). - If your app doesn't need to read FLAC files, you might want to disable this to - reduce the size of your codebase and build time. -*/ -#define JUCE_USE_FLAC 1 - -/** Config: JUCE_USE_OGGVORBIS - Enables the Ogg-Vorbis audio codec classes (available on all platforms). - If your app doesn't need to read Ogg-Vorbis files, you might want to disable this to - reduce the size of your codebase and build time. -*/ -#define JUCE_USE_OGGVORBIS 1 - -/** Config: JUCE_USE_MP3AUDIOFORMAT - Enables the software-based MP3AudioFormat class. - IMPORTANT DISCLAIMER: By choosing to enable the JUCE_USE_MP3AUDIOFORMAT flag and to compile - this MP3 code into your software, you do so AT YOUR OWN RISK! By doing so, you are agreeing - that Raw Material Software is in no way responsible for any patent, copyright, or other - legal issues that you may suffer as a result. - - The code in juce_MP3AudioFormat.cpp is NOT guaranteed to be free from infringements of 3rd-party - intellectual property. If you wish to use it, please seek your own independent advice about the - legality of doing so. If you are not willing to accept full responsibility for the consequences - of using this code, then do not enable this setting. -*/ -#define JUCE_USE_MP3AUDIOFORMAT 0 - -/** Config: JUCE_USE_LAME_AUDIO_FORMAT - Enables the LameEncoderAudioFormat class. -*/ -#define JUCE_USE_LAME_AUDIO_FORMAT 1 - -/** Config: JUCE_USE_WINDOWS_MEDIA_FORMAT - Enables the Windows Media SDK codecs. -*/ -#define JUCE_USE_WINDOWS_MEDIA_FORMAT 0 - -#endif // CARLA_JUCE_AUDIO_FORMATS_APPCONFIG_H_INCLUDED diff --git a/source/modules/juce_audio_formats/Makefile b/source/modules/juce_audio_formats/Makefile index dce2dbb7c..190422561 100644 --- a/source/modules/juce_audio_formats/Makefile +++ b/source/modules/juce_audio_formats/Makefile @@ -10,7 +10,7 @@ include ../Makefile.mk # ---------------------------------------------------------------------------------------------------------------------------- -BUILD_CXX_FLAGS += $(JUCE_AUDIO_FORMATS_FLAGS) -w +BUILD_CXX_FLAGS += $(JUCE_AUDIO_FORMATS_FLAGS) -I.. # ---------------------------------------------------------------------------------------------------------------------------- diff --git a/source/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp index 6a631ddb9..b6cb30639 100644 --- a/source/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp @@ -531,7 +531,7 @@ public: } else if (type == chunkName ("INST")) { - HeapBlock inst; + HeapBlock inst; inst.calloc (jmax ((size_t) length + 1, sizeof (InstChunk)), 1); input->read (inst, (int) length); inst->copyTo (metadataValues); @@ -715,7 +715,7 @@ private: using namespace AiffFileHelpers; const bool couldSeekOk = output->setPosition (headerPosition); - (void) couldSeekOk; + ignoreUnused (couldSeekOk); // if this fails, you've given it an output stream that can't seek! It needs // to be able to seek back to write the header @@ -972,7 +972,7 @@ bool AiffAudioFormat::canHandleFile (const File& f) AudioFormatReader* AiffAudioFormat::createReaderFor (InputStream* sourceStream, const bool deleteStreamIfOpeningFails) { - ScopedPointer w (new AiffAudioFormatReader (sourceStream)); + ScopedPointer w (new AiffAudioFormatReader (sourceStream)); if (w->sampleRate > 0 && w->numChannels > 0) return w.release(); @@ -1003,7 +1003,7 @@ AudioFormatWriter* AiffAudioFormat::createWriterFor (OutputStream* out, const StringPairArray& metadataValues, int /*qualityOptionIndex*/) { - if (getPossibleBitDepths().contains (bitsPerSample)) + if (out != nullptr && getPossibleBitDepths().contains (bitsPerSample)) return new AiffAudioFormatWriter (out, sampleRate, numberOfChannels, (unsigned int) bitsPerSample, metadataValues); return nullptr; diff --git a/source/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp index 34e6a3bb0..43972b60d 100644 --- a/source/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp @@ -433,7 +433,7 @@ public: memcpy (buffer + 18, info.md5sum, 16); const bool seekOk = output->setPosition (4); - (void) seekOk; + ignoreUnused (seekOk); // if this fails, you've given it an output stream that can't seek! It needs // to be able to seek back to write the header @@ -536,7 +536,7 @@ AudioFormatWriter* FlacAudioFormat::createWriterFor (OutputStream* out, const StringPairArray& /*metadataValues*/, int qualityOptionIndex) { - if (getPossibleBitDepths().contains (bitsPerSample)) + if (out != nullptr && getPossibleBitDepths().contains (bitsPerSample)) { ScopedPointer w (new FlacWriter (out, sampleRate, numberOfChannels, (uint32) bitsPerSample, qualityOptionIndex)); diff --git a/source/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp index c7468fbeb..8be87bf68 100644 --- a/source/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp @@ -110,7 +110,7 @@ private: if (cp.start (processArgs)) { const String childOutput (cp.readAllProcessOutput()); - DBG (childOutput); (void) childOutput; + DBG (childOutput); ignoreUnused (childOutput); cp.waitForProcessToFinish (10000); return tempMP3.getFile().getSize() > 0; @@ -205,6 +205,9 @@ AudioFormatWriter* LAMEEncoderAudioFormat::createWriterFor (OutputStream* stream const StringPairArray& metadataValues, int qualityOptionIndex) { + if (streamToWriteTo == nullptr) + return nullptr; + int vbr = 4; int cbr = 0; diff --git a/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp index 8f9f6d654..4921ec089 100644 --- a/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp @@ -1613,7 +1613,7 @@ private: headerParsed = sideParsed = dataParsed = isFreeFormat = wasFreeFormat = false; lastFrameSize = -1; needToSyncBitStream = true; - frameSize = sideInfoSize = dataSize = frameSize = bitIndex = 0; + frameSize = sideInfoSize = dataSize = bitIndex = 0; lastFrameSizeNoPadding = bufferSpaceIndex = 0; bufferPointer = bufferSpace[bufferSpaceIndex] + 512; synthBo = 1; @@ -3020,7 +3020,7 @@ public: } const int numToCopy = jmin (decodedEnd - decodedStart, numSamples); - float* const* const dst = reinterpret_cast (destSamples); + float* const* const dst = reinterpret_cast (destSamples); memcpy (dst[0] + startOffsetInDestBuffer, decoded0 + decodedStart, sizeof (float) * (size_t) numToCopy); if (numDestChannels > 1 && dst[1] != nullptr) diff --git a/source/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp index 6d1f92af2..454156f4f 100644 --- a/source/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp @@ -428,7 +428,7 @@ private: const String s (metadata [name]); if (s.isNotEmpty()) - vorbis_comment_add_tag (&vc, vorbisName, const_cast (s.toRawUTF8())); + vorbis_comment_add_tag (&vc, vorbisName, const_cast (s.toRawUTF8())); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggWriter) @@ -483,8 +483,12 @@ AudioFormatWriter* OggVorbisAudioFormat::createWriterFor (OutputStream* out, const StringPairArray& metadataValues, int qualityOptionIndex) { - ScopedPointer w (new OggWriter (out, sampleRate, numChannels, - (unsigned int) bitsPerSample, qualityOptionIndex, metadataValues)); + if (out == nullptr) + return nullptr; + + ScopedPointer w (new OggWriter (out, sampleRate, numChannels, + (unsigned int) bitsPerSample, + qualityOptionIndex, metadataValues)); return w->ok ? w.release() : nullptr; } diff --git a/source/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.cpp index b87c05d6f..8c270e4e0 100644 --- a/source/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.cpp @@ -142,7 +142,7 @@ public: if (err != noErr) return; - HeapBlock qt_audio_channel_layout; + HeapBlock qt_audio_channel_layout; qt_audio_channel_layout.calloc (output_layout_size, 1); MovieAudioExtractionGetProperty (extractor, @@ -322,8 +322,8 @@ private: Thread::ThreadID lastThreadId; MovieAudioExtractionRef extractor; AudioStreamBasicDescription inputStreamDesc; - HeapBlock bufferList; - HeapBlock dataBuffer; + HeapBlock bufferList; + HeapBlock dataBuffer; Handle dataHandle; //============================================================================== diff --git a/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp index a1f8323d2..75603b152 100644 --- a/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp @@ -845,7 +845,7 @@ namespace WavFileHelpers static MemoryBlock createFrom (const StringPairArray& values) { - const String ISRC (values.getValue (WavAudioFormat::ISRC, String::empty)); + const String ISRC (values.getValue (WavAudioFormat::ISRC, String())); MemoryOutputStream xml; if (ISRC.isNotEmpty()) @@ -1606,7 +1606,7 @@ AudioFormatWriter* WavAudioFormat::createWriterFor (OutputStream* out, double sa unsigned int numChannels, int bitsPerSample, const StringPairArray& metadataValues, int /*qualityOptionIndex*/) { - if (getPossibleBitDepths().contains (bitsPerSample)) + if (out != nullptr && getPossibleBitDepths().contains (bitsPerSample)) return new WavAudioFormatWriter (out, sampleRate, (unsigned int) numChannels, (unsigned int) bitsPerSample, metadataValues); diff --git a/source/modules/juce_audio_formats/format/juce_AudioFormatManager.cpp b/source/modules/juce_audio_formats/format/juce_AudioFormatManager.cpp index c51ebcb27..13b94784b 100644 --- a/source/modules/juce_audio_formats/format/juce_AudioFormatManager.cpp +++ b/source/modules/juce_audio_formats/format/juce_AudioFormatManager.cpp @@ -151,7 +151,7 @@ AudioFormatReader* AudioFormatManager::createReaderFor (InputStream* audioFileSt // use them to open a file! jassert (getNumKnownFormats() > 0); - ScopedPointer in (audioFileStream); + ScopedPointer in (audioFileStream); if (in != nullptr) { diff --git a/source/modules/juce_audio_formats/format/juce_AudioFormatManager.h b/source/modules/juce_audio_formats/format/juce_AudioFormatManager.h index 3ad92131a..2d30bd63f 100644 --- a/source/modules/juce_audio_formats/format/juce_AudioFormatManager.h +++ b/source/modules/juce_audio_formats/format/juce_AudioFormatManager.h @@ -126,8 +126,8 @@ public: The stream that is passed-in must be capable of being repositioned so that all the formats can have a go at opening it. - If none of the registered formats can open the stream, it'll return 0. If it - returns a reader, it's the caller's responsibility to delete the reader. + If none of the registered formats can open the stream, it'll return nullptr. + If it returns a reader, it's the caller's responsibility to delete the reader. */ AudioFormatReader* createReaderFor (InputStream* audioFileStream); diff --git a/source/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp b/source/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp index 508207010..67d7a709f 100644 --- a/source/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp +++ b/source/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp @@ -95,7 +95,7 @@ bool AudioFormatWriter::writeFromAudioReader (AudioFormatReader& reader, } } - if (! write (const_cast (buffers), numToDo)) + if (! write (const_cast (buffers), numToDo)) return false; numSamplesToRead -= numToDo; diff --git a/source/modules/juce_audio_formats/juce_audio_formats.cpp b/source/modules/juce_audio_formats/juce_audio_formats.cpp index 8a157b6a9..370a084fc 100644 --- a/source/modules/juce_audio_formats/juce_audio_formats.cpp +++ b/source/modules/juce_audio_formats/juce_audio_formats.cpp @@ -22,7 +22,7 @@ ============================================================================== */ -#if defined (JUCE_AUDIO_FORMATS_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE +#ifdef JUCE_AUDIO_FORMATS_H_INCLUDED /* 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 @@ -31,10 +31,6 @@ #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_formats.h" diff --git a/source/modules/juce_audio_formats/sampler/juce_Sampler.cpp b/source/modules/juce_audio_formats/sampler/juce_Sampler.cpp index 7440c16f3..d75989ff8 100644 --- a/source/modules/juce_audio_formats/sampler/juce_Sampler.cpp +++ b/source/modules/juce_audio_formats/sampler/juce_Sampler.cpp @@ -93,7 +93,7 @@ void SamplerVoice::startNote (const int midiNoteNumber, SynthesiserSound* s, const int /*currentPitchWheelPosition*/) { - if (const SamplerSound* const sound = dynamic_cast (s)) + if (const SamplerSound* const sound = dynamic_cast (s)) { pitchRatio = pow (2.0, (midiNoteNumber - sound->midiRootNote) / 12.0) * sound->sourceSampleRate / getSampleRate(); @@ -152,7 +152,7 @@ void SamplerVoice::controllerMoved (const int /*controllerNumber*/, //============================================================================== void SamplerVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples) { - if (const SamplerSound* const playingSound = static_cast (getCurrentlyPlayingSound().get())) + if (const SamplerSound* const playingSound = static_cast (getCurrentlyPlayingSound().get())) { const float* const inL = playingSound->data->getReadPointer (0); const float* const inR = playingSound->data->getNumChannels() > 1 diff --git a/source/modules/juce_audio_processors.h b/source/modules/juce_audio_processors.h deleted file mode 100644 index a81527c12..000000000 --- a/source/modules/juce_audio_processors.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Carla Juce setup - * Copyright (C) 2013-2014 Filipe Coelho - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or any later version. - * - * This program 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. - * - * For a full copy of the GNU General Public License see the doc/GPL.txt file. - */ - -#ifndef CARLA_JUCE_AUDIO_PROCESSORS_H_INCLUDED -#define CARLA_JUCE_AUDIO_PROCESSORS_H_INCLUDED - -#include "juce_gui_basics.h" -#include "juce_audio_basics.h" - -// patched to not use gui on non-win/mac -#include "juce_audio_processors/AppConfig.h" -#include "juce_audio_processors/juce_audio_processors.h" - -#endif // CARLA_JUCE_AUDIO_PROCESSORS_H_INCLUDED diff --git a/source/modules/juce_audio_processors/AppConfig.h b/source/modules/juce_audio_processors/AppConfig.h deleted file mode 100755 index 4a111e739..000000000 --- a/source/modules/juce_audio_processors/AppConfig.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - ============================================================================== - - Build options for juce_audio_processors static library - - ============================================================================== -*/ - -#ifndef CARLA_JUCE_AUDIO_PROCESSORS_APPCONFIG_H_INCLUDED -#define CARLA_JUCE_AUDIO_PROCESSORS_APPCONFIG_H_INCLUDED - -#include "../juce_gui_basics/AppConfig.h" -#include "../juce_audio_basics/AppConfig.h" - -//============================================================================= -/** Config: JUCE_PLUGINHOST_VST - Enables the VST audio plugin hosting classes. This requires the Steinberg VST SDK to be - installed on your machine. - - @see VSTPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU -*/ -#ifndef VESTIGE_HEADER -# define JUCE_PLUGINHOST_VST 1 -#else -# define JUCE_PLUGINHOST_VST 0 -#endif - -/** Config: JUCE_PLUGINHOST_VST3 - Enables the VST3 audio plugin hosting classes. This requires the Steinberg VST3 SDK to be - installed on your machine. - - @see VSTPluginFormat, VST3PluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST, JUCE_PLUGINHOST_AU -*/ -#if JUCE_WINDOWS || JUCE_MAC -# define JUCE_PLUGINHOST_VST3 1 -#else -# define JUCE_PLUGINHOST_VST3 0 -#endif - -/** Config: JUCE_PLUGINHOST_AU - Enables the AudioUnit plugin hosting classes. This is Mac-only, of course. - - @see AudioUnitPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST -*/ -#if JUCE_MAC -# define JUCE_PLUGINHOST_AU 1 -#else -# define JUCE_PLUGINHOST_AU 0 -#endif - -#define JUCE_PLUGINHOST_LADSPA 0 - -// Fix MinGW VST3 build -#if JUCE_WINDOWS -# define _set_abort_behavior(...) -#endif - -#endif // CARLA_JUCE_AUDIO_PROCESSORS_APPCONFIG_H_INCLUDED diff --git a/source/modules/juce_audio_processors/Makefile b/source/modules/juce_audio_processors/Makefile index 6d4c6c9bb..c246b23ec 100644 --- a/source/modules/juce_audio_processors/Makefile +++ b/source/modules/juce_audio_processors/Makefile @@ -10,12 +10,13 @@ include ../Makefile.mk # ---------------------------------------------------------------------------------------------------------------------------- -BUILD_CXX_FLAGS += $(JUCE_AUDIO_PROCESSORS_FLAGS) -I$(CWD)/includes/ladspa -I$(CWD)/includes/vst2 -I$(CWD)/includes/vst3 -w +BUILD_CXX_FLAGS += $(JUCE_AUDIO_PROCESSORS_FLAGS) -I$(CWD)/includes/ladspa -I$(CWD)/includes/vst2 -I$(CWD)/includes/vst3 -I.. ifeq ($(CARLA_VESTIGE_HEADER),true) BUILD_CXX_FLAGS += -DVESTIGE_HEADER else # needed by vst3 +BUILD_CXX_FLAGS += -w ifeq ($(DEBUG),true) BUILD_CXX_FLAGS += -DDEVELOPMENT -D_DEBUG else diff --git a/source/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.cpp b/source/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.cpp index e27f128ea..f29e8adea 100644 --- a/source/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.cpp +++ b/source/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.cpp @@ -33,19 +33,19 @@ void AudioPluginFormatManager::addDefaultFormats() for (int i = formats.size(); --i >= 0;) { #if JUCE_PLUGINHOST_VST - jassert (dynamic_cast (formats[i]) == nullptr); + jassert (dynamic_cast (formats[i]) == nullptr); #endif #if JUCE_PLUGINHOST_VST3 - jassert (dynamic_cast (formats[i]) == nullptr); + jassert (dynamic_cast (formats[i]) == nullptr); #endif #if JUCE_PLUGINHOST_AU && JUCE_MAC - jassert (dynamic_cast (formats[i]) == nullptr); + jassert (dynamic_cast (formats[i]) == nullptr); #endif #if JUCE_PLUGINHOST_LADSPA && JUCE_LINUX - jassert (dynamic_cast (formats[i]) == nullptr); + jassert (dynamic_cast (formats[i]) == nullptr); #endif } #endif diff --git a/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm b/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm index 9464344cc..d42534cb6 100644 --- a/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm +++ b/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm @@ -209,7 +209,7 @@ namespace AudioUnitFormatHelpers if (manuString != 0 && CFGetTypeID (manuString) == CFStringGetTypeID()) manufacturer = String::fromCFString ((CFStringRef) manuString); - const short resFileId = CFBundleOpenBundleResourceMap (bundleRef); + const ResFileRefNum resFileId = CFBundleOpenBundleResourceMap (bundleRef); UseResFile (resFileId); const OSType thngType = stringToOSType ("thng"); @@ -379,8 +379,8 @@ public: desc.category = AudioUnitFormatHelpers::getCategory (componentDesc.componentType); desc.manufacturerName = manufacturer; desc.version = version; - desc.numInputChannels = getNumInputChannels(); - desc.numOutputChannels = getNumOutputChannels(); + desc.numInputChannels = getTotalNumInputChannels(); + desc.numOutputChannels = getTotalNumOutputChannels(); desc.isInstrument = (componentDesc.componentType == kAudioUnitType_MusicDevice); } @@ -540,7 +540,7 @@ public: for (AudioUnitElement j = 0; j < numOutputBusChannels; ++j) { abl->mBuffers[j].mNumberChannels = 1; - abl->mBuffers[j].mDataByteSize = sizeof (float) * (size_t) numSamples; + abl->mBuffers[j].mDataByteSize = (UInt32) (sizeof (float) * (size_t) numSamples); abl->mBuffers[j].mData = buffer.getWritePointer ((int) (i * numOutputBusChannels + j)); } } @@ -578,7 +578,7 @@ public: else { // Plugin not working correctly, so just bypass.. - for (int i = 0; i < getNumOutputChannels(); ++i) + for (int i = getTotalNumOutputChannels(); --i >= 0;) buffer.clear (i, 0, buffer.getNumSamples()); } @@ -597,7 +597,7 @@ public: //============================================================================== const String getInputChannelName (int index) const override { - if (isPositiveAndBelow (index, getNumInputChannels())) + if (isPositiveAndBelow (index, getTotalNumInputChannels())) return "Input " + String (index + 1); return String(); @@ -605,14 +605,14 @@ public: const String getOutputChannelName (int index) const override { - if (isPositiveAndBelow (index, getNumOutputChannels())) + if (isPositiveAndBelow (index, getTotalNumOutputChannels())) return "Output " + String (index + 1); return String(); } - bool isInputChannelStereoPair (int index) const override { return isPositiveAndBelow (index, getNumInputChannels()); } - bool isOutputChannelStereoPair (int index) const override { return isPositiveAndBelow (index, getNumOutputChannels()); } + bool isInputChannelStereoPair (int index) const override { return isPositiveAndBelow (index, getTotalNumInputChannels()); } + bool isOutputChannelStereoPair (int index) const override { return isPositiveAndBelow (index, getTotalNumOutputChannels()); } //============================================================================== int getNumParameters() override { return parameters.size(); } @@ -841,9 +841,9 @@ public: if (audioUnit != nullptr) { - UInt32 dummy = 0, paramListSize = 0; - AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, - 0, &dummy, ¶mListSize); + UInt32 paramListSize = 0; + AudioUnitGetPropertyInfo (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, + 0, ¶mListSize, nullptr); if (paramListSize > 0) { @@ -855,7 +855,7 @@ public: AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0, ids, ¶mListSize); - for (int i = 0; i < numParams; ++i) + for (size_t i = 0; i < numParams; ++i) { AudioUnitParameterInfo info; UInt32 sz = sizeof (info); @@ -909,7 +909,7 @@ private: CriticalSection lock; bool wantsMidiMessages, producesMidiMessages, wasPlaying, prepared; - HeapBlock outputBufferList; + HeapBlock outputBufferList; AudioTimeStamp timeStamp; AudioSampleBuffer* currentBuffer; AudioUnitElement numInputBusChannels, numOutputBusChannels, numInputBusses, numOutputBusses; @@ -972,12 +972,7 @@ private: kAudioUnitScope_Global, 0, &info, sizeof (info)); } - AUEventListenerCreate (eventListenerCallback, this, - #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 - CFRunLoopGetMain(), - #else - nullptr, - #endif + AUEventListenerCreate (eventListenerCallback, this, CFRunLoopGetMain(), kCFRunLoopDefaultMode, 0, 0, &eventListenerRef); for (int i = 0; i < parameters.size(); ++i) @@ -1007,6 +1002,10 @@ private: event.mEventType = kAudioUnitEvent_PropertyChange; AUEventListenerAddEventType (eventListenerRef, nullptr, &event); + + // Add a listener for parameter list changes + event.mArgument.mProperty.mPropertyID = kAudioUnitProperty_ParameterList; + AUEventListenerAddEventType (eventListenerRef, nullptr, &event); } } @@ -1037,7 +1036,11 @@ private: break; default: - sendAllParametersChangedEvents(); + if (event.mArgument.mProperty.mPropertyID == kAudioUnitProperty_ParameterList) + updateHostDisplay(); + else if (event.mArgument.mProperty.mPropertyID == kAudioUnitProperty_PresentPreset) + sendAllParametersChangedEvents(); + break; } } @@ -1127,24 +1130,24 @@ private: OSStatus getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat, Float32* outTimeSig_Numerator, UInt32* outTimeSig_Denominator, Float64* outCurrentMeasureDownBeat) const { - AudioPlayHead* const ph = getPlayHead(); - AudioPlayHead::CurrentPositionInfo result; - - if (ph != nullptr && ph->getCurrentPosition (result)) + if (AudioPlayHead* const ph = getPlayHead()) { - setIfNotNull (outTimeSig_Numerator, (UInt32) result.timeSigNumerator); - setIfNotNull (outTimeSig_Denominator, (UInt32) result.timeSigDenominator); - setIfNotNull (outDeltaSampleOffsetToNextBeat, (UInt32) 0); //xxx - setIfNotNull (outCurrentMeasureDownBeat, result.ppqPositionOfLastBarStart); //xxx wrong - } - else - { - setIfNotNull (outDeltaSampleOffsetToNextBeat, (UInt32) 0); - setIfNotNull (outTimeSig_Numerator, (UInt32) 4); - setIfNotNull (outTimeSig_Denominator, (UInt32) 4); - setIfNotNull (outCurrentMeasureDownBeat, 0); + AudioPlayHead::CurrentPositionInfo result; + + if (ph->getCurrentPosition (result)) + { + setIfNotNull (outDeltaSampleOffsetToNextBeat, (UInt32) 0); //xxx + setIfNotNull (outTimeSig_Numerator, (UInt32) result.timeSigNumerator); + setIfNotNull (outTimeSig_Denominator, (UInt32) result.timeSigDenominator); + setIfNotNull (outCurrentMeasureDownBeat, result.ppqPositionOfLastBarStart); //xxx wrong + return noErr; + } } + setIfNotNull (outDeltaSampleOffsetToNextBeat, (UInt32) 0); + setIfNotNull (outTimeSig_Numerator, (UInt32) 4); + setIfNotNull (outTimeSig_Denominator, (UInt32) 4); + setIfNotNull (outCurrentMeasureDownBeat, 0); return noErr; } @@ -1152,34 +1155,34 @@ private: Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling, Float64* outCycleStartBeat, Float64* outCycleEndBeat) { - AudioPlayHead* const ph = getPlayHead(); - AudioPlayHead::CurrentPositionInfo result; - - if (ph != nullptr && ph->getCurrentPosition (result)) + if (AudioPlayHead* const ph = getPlayHead()) { - setIfNotNull (outIsPlaying, result.isPlaying); + AudioPlayHead::CurrentPositionInfo result; - if (outTransportStateChanged != nullptr) + if (ph->getCurrentPosition (result)) { - *outTransportStateChanged = result.isPlaying != wasPlaying; - wasPlaying = result.isPlaying; - } + setIfNotNull (outIsPlaying, result.isPlaying); - setIfNotNull (outCurrentSampleInTimeLine, result.timeInSamples); - setIfNotNull (outIsCycling, false); - setIfNotNull (outCycleStartBeat, 0); - setIfNotNull (outCycleEndBeat, 0); - } - else - { - setIfNotNull (outIsPlaying, false); - setIfNotNull (outTransportStateChanged, false); - setIfNotNull (outCurrentSampleInTimeLine, 0); - setIfNotNull (outIsCycling, false); - setIfNotNull (outCycleStartBeat, 0); - setIfNotNull (outCycleEndBeat, 0); + if (outTransportStateChanged != nullptr) + { + *outTransportStateChanged = result.isPlaying != wasPlaying; + wasPlaying = result.isPlaying; + } + + setIfNotNull (outCurrentSampleInTimeLine, result.timeInSamples); + setIfNotNull (outIsCycling, result.isLooping); + setIfNotNull (outCycleStartBeat, result.ppqLoopStart); + setIfNotNull (outCycleEndBeat, result.ppqLoopEnd); + return noErr; + } } + setIfNotNull (outIsPlaying, false); + setIfNotNull (outTransportStateChanged, false); + setIfNotNull (outCurrentSampleInTimeLine, 0); + setIfNotNull (outIsCycling, false); + setIfNotNull (outCycleStartBeat, 0.0); + setIfNotNull (outCycleEndBeat, 0.0); return noErr; } @@ -1193,7 +1196,7 @@ private: } static OSStatus renderMidiOutputCallback (void* hostRef, const AudioTimeStamp*, UInt32 /*midiOutNum*/, - const struct MIDIPacketList* pktlist) + const MIDIPacketList* pktlist) { return static_cast (hostRef)->renderMidiOutput (pktlist); } @@ -1394,7 +1397,7 @@ private: && AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global, 0, &dataSize, &isWritable) == noErr) { - HeapBlock info; + HeapBlock info; info.calloc (dataSize, 1); if (AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global, diff --git a/source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp b/source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp index bfc53449d..b23f711df 100644 --- a/source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp +++ b/source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp @@ -225,8 +225,8 @@ public: desc.category = getCategory(); desc.manufacturerName = plugin != nullptr ? String (plugin->Maker) : String(); desc.version = getVersion(); - desc.numInputChannels = getNumInputChannels(); - desc.numOutputChannels = getNumOutputChannels(); + desc.numInputChannels = getTotalNumInputChannels(); + desc.numOutputChannels = getTotalNumOutputChannels(); desc.isInstrument = false; } @@ -327,16 +327,16 @@ public: jassertfalse; // no callback to use? } - for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i) + for (int i = getTotalNumInputChannels(), e = getTotalNumOutputChannels(); i < e; ++i) buffer.clear (i, 0, numSamples); } - bool isInputChannelStereoPair (int index) const { return isPositiveAndBelow (index, getNumInputChannels()); } - bool isOutputChannelStereoPair (int index) const { return isPositiveAndBelow (index, getNumInputChannels()); } + bool isInputChannelStereoPair (int index) const { return isPositiveAndBelow (index, getTotalNumInputChannels()); } + bool isOutputChannelStereoPair (int index) const { return isPositiveAndBelow (index, getTotalNumOutputChannels()); } const String getInputChannelName (const int index) const { - if (isPositiveAndBelow (index, getNumInputChannels())) + if (isPositiveAndBelow (index, getTotalNumInputChannels())) return String (plugin->PortNames [inputs [index]]).trim(); return String(); @@ -344,7 +344,7 @@ public: const String getOutputChannelName (const int index) const { - if (isPositiveAndBelow (index, getNumInputChannels())) + if (isPositiveAndBelow (index, getTotalNumInputChannels())) return String (plugin->PortNames [outputs [index]]).trim(); return String(); @@ -451,7 +451,7 @@ public: void setStateInformation (const void* data, int sizeInBytes) { - const float* p = static_cast (data); + const float* p = static_cast (data); for (int i = 0; i < getNumParameters(); ++i) setParameter (i, p[i]); diff --git a/source/modules/juce_audio_processors/format_types/juce_VST3Common.h b/source/modules/juce_audio_processors/format_types/juce_VST3Common.h index ec33c618b..2ede0a636 100644 --- a/source/modules/juce_audio_processors/format_types/juce_VST3Common.h +++ b/source/modules/juce_audio_processors/format_types/juce_VST3Common.h @@ -90,8 +90,8 @@ static Steinberg::Vst::TChar* toString (const juce::String& source) noexcept //============================================================================== -static Steinberg::Vst::SpeakerArrangement getArrangementForBus (Steinberg::Vst::IAudioProcessor* processor, - bool isInput, int busIndex) +static inline Steinberg::Vst::SpeakerArrangement getArrangementForBus (Steinberg::Vst::IAudioProcessor* processor, + bool isInput, int busIndex) { Steinberg::Vst::SpeakerArrangement arrangement = Steinberg::Vst::SpeakerArr::kEmpty; @@ -105,7 +105,7 @@ static Steinberg::Vst::SpeakerArrangement getArrangementForBus (Steinberg::Vst:: /** For the sake of simplicity, there can only be 1 arrangement type per channel count. i.e.: 4 channels == k31Cine OR k40Cine */ -static Steinberg::Vst::SpeakerArrangement getArrangementForNumChannels (int numChannels) noexcept +static inline Steinberg::Vst::SpeakerArrangement getArrangementForNumChannels (int numChannels) noexcept { using namespace Steinberg::Vst::SpeakerArr; @@ -137,6 +137,91 @@ static Steinberg::Vst::SpeakerArrangement getArrangementForNumChannels (int numC return (Steinberg::Vst::SpeakerArrangement) bi.toInt64(); } +static inline Steinberg::Vst::Speaker getSpeakerType (AudioChannelSet::ChannelType type) noexcept +{ + using namespace Steinberg::Vst; + + switch (type) + { + case AudioChannelSet::ChannelType::left: return kSpeakerL; + case AudioChannelSet::ChannelType::right: return kSpeakerR; + case AudioChannelSet::ChannelType::centre: return kSpeakerC; + case AudioChannelSet::ChannelType::subbass: return kSpeakerLfe; + case AudioChannelSet::ChannelType::surroundLeft: return kSpeakerLs; + case AudioChannelSet::ChannelType::surroundRight: return kSpeakerRs; + case AudioChannelSet::ChannelType::centreLeft: return kSpeakerLc; + case AudioChannelSet::ChannelType::centreRight: return kSpeakerRc; + case AudioChannelSet::ChannelType::surround: return kSpeakerS; + case AudioChannelSet::ChannelType::sideLeft: return kSpeakerSl; + case AudioChannelSet::ChannelType::sideRight: return kSpeakerSr; + case AudioChannelSet::ChannelType::topMiddle: return kSpeakerTm; + case AudioChannelSet::ChannelType::topFrontLeft: return kSpeakerTfl; + case AudioChannelSet::ChannelType::topFrontCentre: return kSpeakerTfc; + case AudioChannelSet::ChannelType::topFrontRight: return kSpeakerTfr; + case AudioChannelSet::ChannelType::topRearLeft: return kSpeakerTrl; + case AudioChannelSet::ChannelType::topRearCentre: return kSpeakerTrc; + case AudioChannelSet::ChannelType::topRearRight: return kSpeakerTrr; + case AudioChannelSet::ChannelType::subbass2: return kSpeakerLfe2; + default: break; + } + + return 0; +} + +static inline AudioChannelSet::ChannelType getChannelType (Steinberg::Vst::Speaker type) noexcept +{ + using namespace Steinberg::Vst; + + switch (type) + { + case kSpeakerL: return AudioChannelSet::ChannelType::left; + case kSpeakerR: return AudioChannelSet::ChannelType::right; + case kSpeakerC: return AudioChannelSet::ChannelType::centre; + case kSpeakerLfe: return AudioChannelSet::ChannelType::subbass; + case kSpeakerLs: return AudioChannelSet::ChannelType::surroundLeft; + case kSpeakerRs: return AudioChannelSet::ChannelType::surroundRight; + case kSpeakerLc: return AudioChannelSet::ChannelType::centreLeft; + case kSpeakerRc: return AudioChannelSet::ChannelType::centreRight; + case kSpeakerS: return AudioChannelSet::ChannelType::surround; + case kSpeakerSl: return AudioChannelSet::ChannelType::sideLeft; + case kSpeakerSr: return AudioChannelSet::ChannelType::sideRight; + case kSpeakerTm: return AudioChannelSet::ChannelType::topMiddle; + case kSpeakerTfl: return AudioChannelSet::ChannelType::topFrontLeft; + case kSpeakerTfc: return AudioChannelSet::ChannelType::topFrontCentre; + case kSpeakerTfr: return AudioChannelSet::ChannelType::topFrontRight; + case kSpeakerTrl: return AudioChannelSet::ChannelType::topRearLeft; + case kSpeakerTrc: return AudioChannelSet::ChannelType::topRearCentre; + case kSpeakerTrr: return AudioChannelSet::ChannelType::topRearRight; + case kSpeakerLfe2: return AudioChannelSet::ChannelType::subbass2; + default: break; + } + + return AudioChannelSet::ChannelType::unknown; +} + +static inline Steinberg::Vst::SpeakerArrangement getSpeakerArrangement (const AudioChannelSet& channels) noexcept +{ + Steinberg::Vst::SpeakerArrangement result = 0; + + Array types (channels.getChannelTypes()); + + for (int i = 0; i < types.size(); ++i) + result |= getSpeakerType (types.getReference(i)); + + return result; +} + +static inline AudioChannelSet getChannelSetForSpeakerArrangement (Steinberg::Vst::SpeakerArrangement arr) noexcept +{ + AudioChannelSet result; + + for (Steinberg::Vst::Speaker speaker = 1; speaker <= Steinberg::Vst::kSpeakerRcs; speaker <<= 1) + if ((arr & speaker) != 0) + result.addChannel (getChannelType (speaker)); + + return result; +} + //============================================================================== template class ComSmartPtr diff --git a/source/modules/juce_audio_processors/format_types/juce_VST3Headers.h b/source/modules/juce_audio_processors/format_types/juce_VST3Headers.h index 51a5649ed..987a94f37 100644 --- a/source/modules/juce_audio_processors/format_types/juce_VST3Headers.h +++ b/source/modules/juce_audio_processors/format_types/juce_VST3Headers.h @@ -45,6 +45,9 @@ #pragma clang diagnostic ignored "-Wshadow" #pragma clang diagnostic ignored "-Wdeprecated-register" #pragma clang diagnostic ignored "-Wunused-function" + #pragma clang diagnostic ignored "-Wsign-conversion" + #pragma clang diagnostic ignored "-Wsign-compare" + #pragma clang diagnostic ignored "-Wdelete-non-virtual-dtor" #endif /* These files come with the Steinberg VST3 SDK - to get them, you'll need to @@ -79,6 +82,11 @@ #include #include #else + #if JUCE_MINGW + #define _set_abort_behavior(...) + #endif + #define Point CarbonDummyPointName // The VST headers include some system headers that need + // to match the name our hacky Carbon workaround used. #include #include #include @@ -104,6 +112,7 @@ #include #include #include + #undef Point //============================================================================== namespace Steinberg diff --git a/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp b/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp index 1c661ba74..bd99e52b8 100644 --- a/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp +++ b/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp @@ -455,7 +455,7 @@ public: tresult PLUGIN_API requestOpenEditor (FIDString name) override { - (void) name; + ignoreUnused (name); jassertfalse; return kResultFalse; } @@ -1577,6 +1577,11 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3PluginWindow) }; +#if JUCE_MSVC + #pragma warning (push) + #pragma warning (disable: 4996) // warning about overriding deprecated methods +#endif + //============================================================================== class VST3PluginInstance : public AudioPluginInstance { @@ -1666,8 +1671,8 @@ public: createPluginDescription (description, module->file, company, module->name, *info, info2, infoW, - getNumInputChannels(), - getNumOutputChannels()); + getTotalNumInputChannels(), + getTotalNumOutputChannels()); } void* getPlatformSpecificData() override { return component; } @@ -1676,7 +1681,7 @@ public: //============================================================================== const String getName() const override { - return module != nullptr ? module->name : String::empty; + return module != nullptr ? module->name : String(); } void repopulateArrangements() @@ -1755,19 +1760,15 @@ public: if (! isActive) return; // Avoids redundantly calling things like setActive - JUCE_TRY - { - isActive = false; + isActive = false; - setStateForAllBusses (false); + setStateForAllBusses (false); - if (processor != nullptr) - warnOnFailure (processor->setProcessing (false)); + if (processor != nullptr) + warnOnFailure (processor->setProcessing (false)); - if (component != nullptr) - warnOnFailure (component->setActive (false)); - } - JUCE_CATCH_ALL_ASSERT + if (component != nullptr) + warnOnFailure (component->setActive (false)); } bool supportsDoublePrecisionProcessing() const override @@ -1809,7 +1810,7 @@ public: updateTimingInformation (data, getSampleRate()); - for (int i = getNumInputChannels(); i < buffer.getNumChannels(); ++i) + for (int i = getTotalNumInputChannels(); i < buffer.getNumChannels(); ++i) buffer.clear (i, 0, numSamples); associateTo (data, buffer); @@ -1838,7 +1839,7 @@ public: return toString (busInfo.name); } - return String::empty; + return String(); } const String getInputChannelName (int channelIndex) const override { return getChannelName (channelIndex, true, true); } @@ -1846,18 +1847,14 @@ public: bool isInputChannelStereoPair (int channelIndex) const override { - if (channelIndex < 0 || channelIndex >= getNumInputChannels()) - return false; - - return getBusInfo (true, true).channelCount == 2; + return isPositiveAndBelow (channelIndex, getTotalNumInputChannels()) + && getBusInfo (true, true).channelCount == 2; } bool isOutputChannelStereoPair (int channelIndex) const override { - if (channelIndex < 0 || channelIndex >= getNumOutputChannels()) - return false; - - return getBusInfo (false, true).channelCount == 2; + return isPositiveAndBelow (channelIndex, getTotalNumOutputChannels()) + && getBusInfo (false, true).channelCount == 2; } bool acceptsMidi() const override { return getBusInfo (true, false).channelCount > 0; } @@ -1942,7 +1939,7 @@ public: return toString (result); } - return String::empty; + return String(); } void setParameter (int parameterIndex, float newValue) override @@ -2024,8 +2021,7 @@ public: /** @note Not applicable to VST3 */ void setCurrentProgramStateInformation (const void* data, int sizeInBytes) override { - (void) data; - (void) sizeInBytes; + ignoreUnused (data, sizeInBytes); } //============================================================================== @@ -2461,6 +2457,10 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3PluginInstance) }; +#if JUCE_MSVC + #pragma warning (pop) +#endif + }; //============================================================================== diff --git a/source/modules/juce_audio_processors/format_types/juce_VSTMidiEventList.h b/source/modules/juce_audio_processors/format_types/juce_VSTMidiEventList.h index bf7bc4b53..11cbe4f7f 100644 --- a/source/modules/juce_audio_processors/format_types/juce_VSTMidiEventList.h +++ b/source/modules/juce_audio_processors/format_types/juce_VSTMidiEventList.h @@ -160,7 +160,7 @@ public: } //============================================================================== - HeapBlock events; + HeapBlock events; private: int numEventsUsed, numEventsAllocated; diff --git a/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp b/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp index ee797b832..d7f30db30 100644 --- a/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp +++ b/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp @@ -469,7 +469,7 @@ public: } } - return String::empty; + return String(); } #endif #else @@ -479,7 +479,7 @@ public: Handle resHandle; CFBundleRef bundleRef; FSSpec parentDirFSSpec; - short resFileId; + ResFileRefNum resFileId; bool open() { @@ -701,6 +701,11 @@ private: static const int defaultVSTSampleRateValue = 44100; static const int defaultVSTBlockSizeValue = 512; +#if JUCE_MSVC + #pragma warning (push) + #pragma warning (disable: 4996) // warning about overriding deprecated methods +#endif + //============================================================================== //============================================================================== class VSTPluginInstance : public AudioPluginInstance, @@ -815,8 +820,8 @@ public: } desc.version = getVersion(); - desc.numInputChannels = getNumInputChannels(); - desc.numOutputChannels = getNumOutputChannels(); + desc.numInputChannels = getTotalNumInputChannels(); + desc.numOutputChannels = getTotalNumOutputChannels(); desc.isInstrument = (effect != nullptr && (effect->flags & effFlagsIsSynth) != 0); } @@ -1017,19 +1022,19 @@ public: //============================================================================== const String getInputChannelName (int index) const override { - if (index >= 0 && index < getNumInputChannels()) + if (isValidChannel (index, true)) { VstPinProperties pinProps; if (dispatch (effGetInputProperties, index, 0, &pinProps, 0.0f) != 0) return String (pinProps.label, sizeof (pinProps.label)); } - return String::empty; + return String(); } bool isInputChannelStereoPair (int index) const override { - if (index < 0 || index >= getNumInputChannels()) + if (! isValidChannel (index, true)) return false; VstPinProperties pinProps; @@ -1041,19 +1046,19 @@ public: const String getOutputChannelName (int index) const override { - if (index >= 0 && index < getNumOutputChannels()) + if (isValidChannel (index, false)) { VstPinProperties pinProps; if (dispatch (effGetOutputProperties, index, 0, &pinProps, 0.0f) != 0) return String (pinProps.label, sizeof (pinProps.label)); } - return String::empty; + return String(); } bool isOutputChannelStereoPair (int index) const override { - if (index < 0 || index >= getNumOutputChannels()) + if (! isValidChannel (index, false)) return false; VstPinProperties pinProps; @@ -1063,9 +1068,10 @@ public: return true; } - bool isValidChannel (int index, bool isInput) const + bool isValidChannel (int index, bool isInput) const noexcept { - return isPositiveAndBelow (index, isInput ? getNumInputChannels() : getNumOutputChannels()); + return isPositiveAndBelow (index, isInput ? getTotalNumInputChannels() + : getTotalNumOutputChannels()); } //============================================================================== @@ -1686,7 +1692,7 @@ private: else { // Not initialised, so just bypass.. - for (int i = 0; i < getNumOutputChannels(); ++i) + for (int i = getTotalNumOutputChannels(); --i >= 0;) buffer.clear (i, 0, buffer.getNumSamples()); } @@ -1750,7 +1756,7 @@ private: String getTextForOpcode (const int index, const AEffectOpcodes opcode) const { if (effect == nullptr) - return String::empty; + return String(); jassert (index >= 0 && index < effect->numParams); char nm [256] = { 0 }; @@ -1775,7 +1781,7 @@ private: if (index >= 0 && programNames[index].isEmpty()) { while (programNames.size() < index) - programNames.add (String::empty); + programNames.add (String()); programNames.set (index, progName); } @@ -2157,7 +2163,7 @@ public: //============================================================================== void mouseDown (const MouseEvent& e) override { - (void) e; + ignoreUnused (e); #if JUCE_LINUX if (pluginWindow == 0) @@ -2376,6 +2382,10 @@ private: { if (isOpen) { + // You shouldn't end up hitting this assertion unless the host is trying to do GUI + // cleanup on a non-GUI thread.. If it does that, bad things could happen in here.. + jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager()); + JUCE_VST_LOG ("Closing VST UI: " + plugin.getName()); isOpen = false; dispatch (effEditClose, 0, 0, 0, 0); @@ -2676,6 +2686,10 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSTPluginWindow) }; +#if JUCE_MSVC + #pragma warning (pop) +#endif + //============================================================================== AudioProcessorEditor* VSTPluginInstance::createEditor() { diff --git a/source/modules/juce_audio_processors/juce_audio_processors.cpp b/source/modules/juce_audio_processors/juce_audio_processors.cpp index 29b2362a1..dd62b2e90 100644 --- a/source/modules/juce_audio_processors/juce_audio_processors.cpp +++ b/source/modules/juce_audio_processors/juce_audio_processors.cpp @@ -22,7 +22,7 @@ ============================================================================== */ -#if defined (JUCE_AUDIO_PROCESSORS_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE +#ifdef JUCE_AUDIO_PROCESSORS_H_INCLUDED /* 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 @@ -31,10 +31,6 @@ #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_processors.h" #include "../juce_gui_extra/juce_gui_extra.h" @@ -155,6 +151,7 @@ void AutoResizingNSViewComponentWithParent::timerCallback() #include "format/juce_AudioPluginFormat.cpp" #include "format/juce_AudioPluginFormatManager.cpp" #include "processors/juce_AudioProcessor.cpp" +#include "processors/juce_AudioChannelSet.cpp" #include "processors/juce_AudioProcessorEditor.cpp" #include "processors/juce_AudioProcessorGraph.cpp" #include "processors/juce_GenericAudioProcessorEditor.cpp" diff --git a/source/modules/juce_audio_processors/juce_audio_processors.h b/source/modules/juce_audio_processors/juce_audio_processors.h index c7c312f7a..9d91ec7f8 100644 --- a/source/modules/juce_audio_processors/juce_audio_processors.h +++ b/source/modules/juce_audio_processors/juce_audio_processors.h @@ -77,6 +77,7 @@ class AudioProcessor; #include "processors/juce_AudioProcessorEditor.h" #include "processors/juce_AudioProcessorListener.h" #include "processors/juce_AudioProcessorParameter.h" +#include "processors/juce_AudioChannelSet.h" #include "processors/juce_AudioProcessor.h" #include "processors/juce_PluginDescription.h" #include "processors/juce_AudioPluginInstance.h" diff --git a/source/modules/juce_audio_processors/processors/juce_AudioChannelSet.cpp b/source/modules/juce_audio_processors/processors/juce_AudioChannelSet.cpp new file mode 100644 index 000000000..2186bbcee --- /dev/null +++ b/source/modules/juce_audio_processors/processors/juce_AudioChannelSet.cpp @@ -0,0 +1,183 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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. + + ============================================================================== +*/ + +AudioChannelSet::AudioChannelSet (uint32 c) : channels (c) {} + +bool AudioChannelSet::operator== (const AudioChannelSet& other) const noexcept { return channels == other.channels; } +bool AudioChannelSet::operator!= (const AudioChannelSet& other) const noexcept { return channels != other.channels; } +bool AudioChannelSet::operator< (const AudioChannelSet& other) const noexcept { return channels < other.channels; } + +const char* AudioChannelSet::getChannelTypeName (AudioChannelSet::ChannelType type) noexcept +{ + switch (type) + { + case left: return NEEDS_TRANS("Left"); + case right: return NEEDS_TRANS("Right"); + case centre: return NEEDS_TRANS("Centre"); + case subbass: return NEEDS_TRANS("Subbass"); + case surroundLeft: return NEEDS_TRANS("Left Surround"); + case surroundRight: return NEEDS_TRANS("Right Surround"); + case centreLeft: return NEEDS_TRANS("Centre Left"); + case centreRight: return NEEDS_TRANS("Centre Right"); + case surround: return NEEDS_TRANS("Surround"); + case sideLeft: return NEEDS_TRANS("Side Left"); + case sideRight: return NEEDS_TRANS("Side Right"); + case topMiddle: return NEEDS_TRANS("Top Middle"); + case topFrontLeft: return NEEDS_TRANS("Top Front Left"); + case topFrontCentre: return NEEDS_TRANS("Top Front Centre"); + case topFrontRight: return NEEDS_TRANS("Top Front Right"); + case topRearLeft: return NEEDS_TRANS("Top Rear Left"); + case topRearCentre: return NEEDS_TRANS("Top Rear Centre"); + case topRearRight: return NEEDS_TRANS("Top Rear Right"); + case wideLeft: return NEEDS_TRANS("Wide Left"); + case wideRight: return NEEDS_TRANS("Wide Right"); + case subbass2: return NEEDS_TRANS("Subbass 2"); + case ambisonicW: return NEEDS_TRANS("Ambisonic W"); + case ambisonicX: return NEEDS_TRANS("Ambisonic X"); + case ambisonicY: return NEEDS_TRANS("Ambisonic Y"); + case ambisonicZ: return NEEDS_TRANS("Ambisonic Z"); + default: break; + } + + return "Unknown"; +} + +const char* AudioChannelSet::getAbbreviatedChannelTypeName (AudioChannelSet::ChannelType type) noexcept +{ + switch (type) + { + case left: return "L"; + case right: return "R"; + case centre: return "C"; + case subbass: return "Lfe"; + case surroundLeft: return "Ls"; + case surroundRight: return "Rs"; + case centreLeft: return "Lc"; + case centreRight: return "Rc"; + case surround: return "S"; + case sideLeft: return "Sl"; + case sideRight: return "Sr"; + case topMiddle: return "Tm"; + case topFrontLeft: return "Tfl"; + case topFrontCentre: return "Tfc"; + case topFrontRight: return "Tfr"; + case topRearLeft: return "Trl"; + case topRearCentre: return "Trc"; + case topRearRight: return "Trr"; + case wideLeft: return "Wl"; + case wideRight: return "Wr"; + case subbass2: return "Lfe2"; + case ambisonicW: return "W"; + case ambisonicX: return "X"; + case ambisonicY: return "Y"; + case ambisonicZ: return "Z"; + default: break; + } + + return ""; +} + +String AudioChannelSet::getSpeakerArrangementAsString() const +{ + StringArray speakerTypes; + Array speakers = getChannelTypes(); + + for (int i = 0; i < speakers.size(); ++i) + { + String name = getAbbreviatedChannelTypeName (speakers.getReference (i)); + + if (name.isNotEmpty()) + speakerTypes.add (name); + } + + return speakerTypes.joinIntoString (" "); +} + +int AudioChannelSet::size() const noexcept +{ + return channels.countNumberOfSetBits(); +} + +AudioChannelSet::ChannelType AudioChannelSet::getTypeOfChannel (int index) const noexcept +{ + int bit = channels.findNextSetBit(0); + + for (int i = 0; i < index && bit >= 0; ++i) + bit = channels.findNextSetBit (bit + 1); + + return static_cast (bit); +} + +Array AudioChannelSet::getChannelTypes() const +{ + Array result; + + for (int bit = channels.findNextSetBit(0); bit >= 0; bit = channels.findNextSetBit (bit + 1)) + result.add (static_cast (bit)); + + return result; +} + +void AudioChannelSet::addChannel (ChannelType newChannel) +{ + const int bit = static_cast (newChannel); + jassert (bit >= 0 && bit < 1024); + channels.setBit (bit); +} + +AudioChannelSet AudioChannelSet::disabled() { return AudioChannelSet(); } +AudioChannelSet AudioChannelSet::mono() { return AudioChannelSet (1u << centre); } +AudioChannelSet AudioChannelSet::stereo() { return AudioChannelSet ((1u << left) | (1u << right)); } +AudioChannelSet AudioChannelSet::createLCR() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre)); } +AudioChannelSet AudioChannelSet::createLCRS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surround)); } +AudioChannelSet AudioChannelSet::quadraphonic() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << sideLeft) | (1u << sideRight)); } +AudioChannelSet AudioChannelSet::pentagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << sideLeft) | (1u << sideRight) | (1u << centre)); } +AudioChannelSet AudioChannelSet::hexagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << sideLeft) | (1u << sideRight) | (1u << centre) | (1u << surround)); } +AudioChannelSet AudioChannelSet::octagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << sideLeft) | (1u << sideRight) | (1u << centre) | (1u << surround) | (1u << wideLeft) | (1u << wideRight)); } +AudioChannelSet AudioChannelSet::ambisonic() { return AudioChannelSet ((1u << ambisonicW) | (1u << ambisonicX) | (1u << ambisonicY) | (1u << ambisonicZ)); } +AudioChannelSet AudioChannelSet::create5point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surroundLeft) | (1u << surroundRight)); } +AudioChannelSet AudioChannelSet::create5point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << surroundLeft) | (1u << surroundRight)); } +AudioChannelSet AudioChannelSet::create6point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surroundLeft) | (1u << surroundRight) | (1u << surround)); } +AudioChannelSet AudioChannelSet::create6point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << surroundLeft) | (1u << surroundRight) | (1u << surround)); } +AudioChannelSet AudioChannelSet::create7point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surroundLeft) | (1u << surroundRight) | (1u << topRearLeft) | (1u << topRearRight)); } +AudioChannelSet AudioChannelSet::create7point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << surroundLeft) | (1u << surroundRight) | (1u << topRearLeft) | (1u << topRearRight)); } +AudioChannelSet AudioChannelSet::createFront7point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surroundLeft) | (1u << surroundRight) | (1u << centreLeft) | (1u << centreRight)); } +AudioChannelSet AudioChannelSet::createFront7point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << surroundLeft) | (1u << surroundRight) | (1u << centreLeft) | (1u << centreRight)); } + + +AudioChannelSet AudioChannelSet::discreteChannels (int numChannels) +{ + AudioChannelSet s; + s.channels.setRange (discreteChannel0, numChannels, true); + return s; +} + +AudioChannelSet AudioChannelSet::canonicalChannelSet (int numChannels) +{ + if (numChannels == 1) return AudioChannelSet::mono(); + if (numChannels == 2) return AudioChannelSet::stereo(); + if (numChannels == 4) return AudioChannelSet::quadraphonic(); + + return discreteChannels (numChannels); +} diff --git a/source/modules/juce_audio_processors/processors/juce_AudioChannelSet.h b/source/modules/juce_audio_processors/processors/juce_AudioChannelSet.h new file mode 100644 index 000000000..886abcc86 --- /dev/null +++ b/source/modules/juce_audio_processors/processors/juce_AudioChannelSet.h @@ -0,0 +1,187 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI 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_AUDIOCHANNELSET_H_INCLUDED +#define JUCE_AUDIOCHANNELSET_H_INCLUDED + + +//============================================================================== +/** + Represents a set of audio channel types. + + For example, you might have a set of left + right channels, which is a stereo + channel set. It is a collection of values from the AudioChannelSet::ChannelType + enum, where each type may only occur once within the set. + + @see AudioProcessorBus +*/ +class JUCE_API AudioChannelSet +{ +public: + /** Creates an empty channel set. + You can call addChannel to add channels to the set. + */ + AudioChannelSet() noexcept {} + + /** Creates a zero-channel set which can be used to indicate that a + bus is disabled. */ + static AudioChannelSet disabled(); + + /** Creates a one-channel mono set. */ + static AudioChannelSet mono(); + + /** Creates a set containing a left and right channel. */ + static AudioChannelSet stereo(); + + /** Creates a set containing a left, right and centre channels. */ + static AudioChannelSet createLCR(); + + /** Creates a set containing a left, right, centre and surround channels. */ + static AudioChannelSet createLCRS(); + + /** Creates a set for quadraphonic surround setup. */ + static AudioChannelSet quadraphonic(); + + /** Creates a set for pentagonal surround setup. */ + static AudioChannelSet pentagonal(); + + /** Creates a set for hexagonal surround setup. */ + static AudioChannelSet hexagonal(); + + /** Creates a set for octagonal surround setup. */ + static AudioChannelSet octagonal(); + + /** Creates a set for ambisonic surround setups. */ + static AudioChannelSet ambisonic(); + + /** Creates a set for a 5.0 surround setup. */ + static AudioChannelSet create5point0(); + + /** Creates a set for a 5.1 surround setup. */ + static AudioChannelSet create5point1(); + + /** Creates a set for a 6.0 surround setup. */ + static AudioChannelSet create6point0(); + + /** Creates a set for a 6.1 surround setup. */ + static AudioChannelSet create6point1(); + + /** Creates a set for a 7.0 surround setup. */ + static AudioChannelSet create7point0(); + + /** Creates a set for a 7.1 surround setup. */ + static AudioChannelSet create7point1(); + + /** Creates a set for a 7.0 surround setup (with side instead of rear speakers). */ + static AudioChannelSet createFront7point0(); + + /** Creates a set for a 7.1 surround setup (with side instead of rear speakers). */ + static AudioChannelSet createFront7point1(); + + /** Creates a set of untyped discrete channels. */ + static AudioChannelSet discreteChannels (int numChannels); + + /** Create a canonical channel set for a given number of channels. + For example, numChannels = 1 will return mono, numChannels = 2 will return stereo, etc. */ + static AudioChannelSet canonicalChannelSet (int numChannels); + + //============================================================================== + /** Represents different audio channel types. */ + enum ChannelType + { + unknown = 0, + + left = 1, + right = 2, + centre = 3, + + subbass = 4, + surroundLeft = 5, + surroundRight = 6, + centreLeft = 7, + centreRight = 8, + surround = 9, + sideLeft = 10, + sideRight = 11, + topMiddle = 12, + topFrontLeft = 13, + topFrontCentre = 14, + topFrontRight = 15, + topRearLeft = 16, + topRearCentre = 17, + topRearRight = 18, + wideLeft = 19, + wideRight = 20, + subbass2 = 21, + + ambisonicW = 22, + ambisonicX = 23, + ambisonicY = 24, + ambisonicZ = 25, + + + discreteChannel0 = 64 /**< Non-typed individual channels are indexed upwards from this value. */ + }; + + /** Returns the name of a given channel type. For example, this method may return "Surround Left". */ + static const char* getChannelTypeName (ChannelType) noexcept; + + /** Returns the abbreviated name of a channel type. For example, this method may return "Ls". */ + static const char* getAbbreviatedChannelTypeName (ChannelType) noexcept; + + //============================================================================== + /** Adds a channel to the set. */ + void addChannel (ChannelType newChannelType); + + /** Returns the number of channels in the set. */ + int size() const noexcept; + + /** Returns the number of channels in the set. */ + bool isDisabled() const noexcept { return size() == 0; } + + /** Returns an array of all the types in this channel set. */ + Array getChannelTypes() const; + + /** Returns the type of one of the channels in the set, by index. */ + ChannelType getTypeOfChannel (int channelIndex) const noexcept; + + /** Returns a string containing a whitespace-separated list of speaker types + corresponding to each channel. For example in a 5.1 arrangement, + the string may be "L R C Lfe Ls Rs". If the speaker arrangement is unknown, + the returned string will be empty.*/ + String getSpeakerArrangementAsString() const; + + //============================================================================== + bool operator== (const AudioChannelSet&) const noexcept; + bool operator!= (const AudioChannelSet&) const noexcept; + bool operator< (const AudioChannelSet&) const noexcept; +private: + BigInteger channels; + + explicit AudioChannelSet (uint32); +}; + + + +#endif // JUCE_AUDIOCHANNELSET_H_INCLUDED diff --git a/source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp b/source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp index 322cdc415..b39e35d11 100644 --- a/source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp +++ b/source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp @@ -34,13 +34,32 @@ AudioProcessor::AudioProcessor() playHead (nullptr), sampleRate (0), blockSize (0), - numInputChannels (0), - numOutputChannels (0), latencySamples (0), + #if JUCE_DEBUG + textRecursionCheck (false), + #endif suspended (false), nonRealtime (false), processingPrecision (singlePrecision) { + #if ! JucePlugin_IsMidiEffect + #ifdef JucePlugin_PreferredChannelConfigurations + const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations }; + #else + const short channelConfigs[][2] = { {2, 2} }; + #endif + int numChannelConfigs = sizeof (channelConfigs) / sizeof (*channelConfigs); + + if (numChannelConfigs > 0) + { + #if ! JucePlugin_IsSynth + busArrangement.inputBuses.add (AudioProcessorBus ("Input", AudioChannelSet::canonicalChannelSet (channelConfigs[0][0]))); + #endif + busArrangement.outputBuses.add (AudioProcessorBus ("Output", AudioChannelSet::canonicalChannelSet (channelConfigs[0][1]))); + } + #endif + + updateSpeakerFormatStrings(); } AudioProcessor::~AudioProcessor() @@ -78,28 +97,50 @@ void AudioProcessor::removeListener (AudioProcessorListener* const listenerToRem void AudioProcessor::setPlayConfigDetails (const int newNumIns, const int newNumOuts, const double newSampleRate, - const int newBlockSize) noexcept + const int newBlockSize) { - sampleRate = newSampleRate; - blockSize = newBlockSize; + const int oldNumInputs = getTotalNumInputChannels(); + const int oldNumOutputs = getTotalNumOutputChannels(); - if (numInputChannels != newNumIns || numOutputChannels != newNumOuts) - { - numInputChannels = newNumIns; - numOutputChannels = newNumOuts; + // if the user is using this method then they do not want any side-buses or aux outputs + disableNonMainBuses (true); + disableNonMainBuses (false); + + if (getTotalNumInputChannels() != newNumIns) setPreferredBusArrangement (true, 0, AudioChannelSet::canonicalChannelSet (newNumIns)); + if (getTotalNumOutputChannels() != newNumOuts) setPreferredBusArrangement (false, 0, AudioChannelSet::canonicalChannelSet (newNumOuts)); + + // the processor may not support this arrangement at all + jassert (newNumIns == getTotalNumInputChannels() && newNumOuts == getTotalNumOutputChannels()); + setRateAndBufferSizeDetails (newSampleRate, newBlockSize); + + if (oldNumInputs != newNumIns || oldNumOutputs != newNumOuts) + { + updateSpeakerFormatStrings(); numChannelsChanged(); } } -void AudioProcessor::numChannelsChanged() {} +void AudioProcessor::setRateAndBufferSizeDetails (double newSampleRate, int newBlockSize) noexcept +{ + sampleRate = newSampleRate; + blockSize = newBlockSize; +} -void AudioProcessor::setSpeakerArrangement (const String& inputs, const String& outputs) +int AudioProcessor::getMainBusNumInputChannels() const noexcept { - inputSpeakerArrangement = inputs; - outputSpeakerArrangement = outputs; + const Array& buses = busArrangement.inputBuses; + return buses.size() > 0 ? buses.getReference (0).channels.size() : 0; } +int AudioProcessor::getMainBusNumOutputChannels() const noexcept +{ + const Array& buses = busArrangement.outputBuses; + return buses.size() > 0 ? buses.getReference (0).channels.size() : 0; +} + +void AudioProcessor::numChannelsChanged() {} + void AudioProcessor::setNonRealtime (const bool newNonRealtime) noexcept { nonRealtime = newNonRealtime; @@ -241,6 +282,13 @@ String AudioProcessor::getParameterName (int index, int maximumStringLength) const String AudioProcessor::getParameterText (int index) { + #if JUCE_DEBUG + // if you hit this, then you're probably using the old parameter control methods, + // but have forgotten to implement either of the getParameterText() methods. + jassert (! textRecursionCheck); + ScopedValueSetter sv (textRecursionCheck, true, false); + #endif + return getParameterText (index, 1024); } @@ -354,6 +402,145 @@ bool AudioProcessor::supportsDoublePrecisionProcessing() const return false; } +//============================================================================== +static String getChannelName (const Array& buses, int index) +{ + return buses.size() > 0 ? AudioChannelSet::getChannelTypeName (buses.getReference(0).channels.getTypeOfChannel (index)) + : String(); +} + +const String AudioProcessor::getInputChannelName (int index) const { return getChannelName (busArrangement.inputBuses, index); } +const String AudioProcessor::getOutputChannelName (int index) const { return getChannelName (busArrangement.outputBuses, index); } + +static bool isStereoPair (const Array& buses, int index) +{ + return index < 2 + && buses.size() > 0 + && buses.getReference(0).channels == AudioChannelSet::stereo(); +} + +bool AudioProcessor::isInputChannelStereoPair (int index) const { return isStereoPair (busArrangement.inputBuses, index); } +bool AudioProcessor::isOutputChannelStereoPair (int index) const { return isStereoPair (busArrangement.outputBuses, index); } + +//============================================================================== +bool AudioProcessor::setPreferredBusArrangement (bool isInput, int busIndex, const AudioChannelSet& preferredSet) +{ + const int oldNumInputs = getTotalNumInputChannels(); + const int oldNumOutputs = getTotalNumOutputChannels(); + + Array& buses = isInput ? busArrangement.inputBuses : busArrangement.outputBuses; + + const int numBuses = buses.size(); + + if (! isPositiveAndBelow (busIndex, numBuses)) + return false; + + AudioProcessorBus& bus = buses.getReference (busIndex); + + #ifdef JucePlugin_PreferredChannelConfigurations + // the user is using the deprecated way to specify channel configurations + if (numBuses > 0 && busIndex == 0) + { + const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations }; + const int numChannelConfigs = sizeof (channelConfigs) / sizeof (*channelConfigs); + + // we need the main bus in the opposite direction + Array& oppositeBuses = isInput ? busArrangement.outputBuses : busArrangement.inputBuses; + AudioProcessorBus* oppositeBus = (busIndex < oppositeBuses.size()) ? &oppositeBuses.getReference (0) : nullptr; + + // get the target number of channels + const int mainBusNumChannels = preferredSet.size(); + const int mainBusOppositeChannels = (oppositeBus != nullptr) ? oppositeBus->channels.size() : 0; + const int dir = isInput ? 0 : 1; + + // find a compatible channel configuration on the opposite bus which is the closest match + // to the current number of channels on that bus + int distance = std::numeric_limits::max(); + int bestConfiguration = -1; + + for (int i = 0; i < numChannelConfigs; ++i) + { + // is the configuration compatible with the preferred set + if (channelConfigs[i][dir] == mainBusNumChannels) + { + const int configChannels = channelConfigs[i][dir^1]; + const int channelDifference = std::abs (configChannels - mainBusOppositeChannels); + + if (channelDifference < distance) + { + distance = channelDifference; + bestConfiguration = configChannels; + + // we can exit if we found a perfect match + if (distance == 0) + break; + } + } + } + + // unable to find a good configuration + if (bestConfiguration == -1) + return false; + + // did the number of channels change on the opposite bus? + if (mainBusOppositeChannels != bestConfiguration && oppositeBus != nullptr) + { + // if the channels on the opposite bus are the same as the preferred set + // then also copy over the layout information. If not, then assume + // a cononical channel layout + if (bestConfiguration == mainBusNumChannels) + oppositeBus->channels = preferredSet; + else + oppositeBus->channels = AudioChannelSet::canonicalChannelSet (bestConfiguration); + } + } + #endif + + bus.channels = preferredSet; + + if (oldNumInputs != getTotalNumInputChannels() || oldNumOutputs != getTotalNumOutputChannels()) + { + updateSpeakerFormatStrings(); + numChannelsChanged(); + } + + return true; +} + +void AudioProcessor::disableNonMainBuses (bool isInput) +{ + const Array& buses = (isInput ? busArrangement.inputBuses : busArrangement.outputBuses); + + for (int busIdx = 1; busIdx < buses.size(); ++busIdx) + { + if (buses.getReference (busIdx).channels != AudioChannelSet::disabled()) + { + bool success = setPreferredBusArrangement (isInput, busIdx, AudioChannelSet::disabled()); + + ignoreUnused (success); + // You are using the setPlayConfigDetails method which should only be used on processors + // with no aux outputs and sidechains. Please use setRateAndBufferSizeDetails and + // setPreferredBusArrangement instead. + jassert (success); + } + } +} + +// Unfortunately the deprecated getInputSpeakerArrangement/getOutputSpeakerArrangement return +// references to strings. Therefore we need to keep a copy. Once getInputSpeakerArrangement is +// removed, we can also remove this function +void AudioProcessor::updateSpeakerFormatStrings() +{ + cachedInputSpeakerArrString.clear(); + cachedOutputSpeakerArrString.clear(); + + if (busArrangement.inputBuses.size() > 0) + cachedInputSpeakerArrString = busArrangement.inputBuses. getReference (0).channels.getSpeakerArrangementAsString(); + + if (busArrangement.outputBuses.size() > 0) + cachedOutputSpeakerArrString = busArrangement.outputBuses.getReference (0).channels.getSpeakerArrangementAsString(); +} + #if ! JUCE_AUDIO_PROCESSOR_NO_GUI //============================================================================== void AudioProcessor::editorBeingDeleted (AudioProcessorEditor* const editor) noexcept @@ -432,6 +619,36 @@ XmlElement* AudioProcessor::getXmlFromBinary (const void* data, const int sizeIn return nullptr; } +//============================================================================== +int AudioProcessor::AudioBusArrangement::getChannelIndexInProcessBlockBuffer (bool isInput, int busIndex, int channelIndex) const noexcept +{ + const Array& ioBus = isInput ? inputBuses : outputBuses; + jassert (busIndex < ioBus.size()); + + for (int i = 0; i < ioBus.size() && i < busIndex; ++i) + channelIndex += ioBus.getReference(i).channels.size(); + + return channelIndex; +} + +static int countTotalChannels (const Array& buses) noexcept +{ + int n = 0; + + for (int i = 0; i < buses.size(); ++i) + n += buses.getReference(i).channels.size(); + + return n; +} + +int AudioProcessor::AudioBusArrangement::getTotalNumInputChannels() const noexcept { return countTotalChannels (inputBuses); } +int AudioProcessor::AudioBusArrangement::getTotalNumOutputChannels() const noexcept { return countTotalChannels (outputBuses); } + +AudioProcessor::AudioProcessorBus::AudioProcessorBus (const String& nm, const AudioChannelSet& chans) + : name (nm), channels (chans) +{ +} + //============================================================================== void AudioProcessorListener::audioProcessorParameterChangeGestureBegin (AudioProcessor*, int) {} void AudioProcessorListener::audioProcessorParameterChangeGestureEnd (AudioProcessor*, int) {} diff --git a/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h b/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h index 4443dec35..509bafb1f 100644 --- a/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h +++ b/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h @@ -69,10 +69,16 @@ public: The sample rate is the target sample rate, and will remain constant until playback stops. + You can call getTotalNumInputChannels and getTotalNumOutputChannels + or query the busArrangement member variable to find out the number of + channels your processBlock callback must process. + The estimatedSamplesPerBlock value is a HINT about the typical number of samples that will be processed for each callback, but isn't any kind of guarantee. The actual block sizes that the host uses may be different each time the callback happens, and may be more or less than this value. + + @see busArrangement, getTotalNumInputChannels, getTotalNumOutputChannels */ virtual void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock) = 0; @@ -89,16 +95,21 @@ public: this filter is using. It will be filled with the filter's input data and should be replaced with the filter's output. - So for example if your filter has 2 input channels and 4 output channels, then - the buffer will contain 4 channels, the first two being filled with the - input data. Your filter should read these, do its processing, and replace - the contents of all 4 channels with its output. + So for example if your filter has a total of 2 input channels and 4 output + channels, then the buffer will contain 4 channels, the first two being filled + with the input data. Your filter should read these, do its processing, and + replace the contents of all 4 channels with its output. - Or if your filter has 5 inputs and 2 outputs, the buffer will have 5 channels, - all filled with data, and your filter should overwrite the first 2 of these - with its output. But be VERY careful not to write anything to the last 3 + Or if your filter has a total of 5 inputs and 2 outputs, the buffer will have 5 + channels, all filled with data, and your filter should overwrite the first 2 of + these with its output. But be VERY careful not to write anything to the last 3 channels, as these might be mapped to memory that the host assumes is read-only! + If your plug-in has more than one input or output buses then the buffer passed + to the processBlock methods will contain a bundle of all channels of each bus. + Use AudioBusArrangement::getBusBuffer to obtain an audio buffer for a + particular bus. + Note that if you have more outputs than inputs, then only those channels that correspond to an input channel are guaranteed to contain sensible data - e.g. in the case of 2 inputs and 4 outputs, the first two channels contain the input, @@ -132,7 +143,10 @@ public: the UI components register as listeners, and then call sendChangeMessage() inside the processBlock() method to send out an asynchronous message. You could also use the AsyncUpdater class in a similar way. + + @see AudioBusArrangement::getBusBuffer */ + virtual void processBlock (AudioBuffer& buffer, MidiBuffer& midiMessages) = 0; @@ -143,16 +157,21 @@ public: this filter is using. It will be filled with the filter's input data and should be replaced with the filter's output. - So for example if your filter has 2 input channels and 4 output channels, then - the buffer will contain 4 channels, the first two being filled with the - input data. Your filter should read these, do its processing, and replace - the contents of all 4 channels with its output. + So for example if your filter has a combined total of 2 input channels and + 4 output channels, then the buffer will contain 4 channels, the first two + being filled with the input data. Your filter should read these, do its + processing, and replace the contents of all 4 channels with its output. Or if your filter has 5 inputs and 2 outputs, the buffer will have 5 channels, all filled with data, and your filter should overwrite the first 2 of these with its output. But be VERY careful not to write anything to the last 3 channels, as these might be mapped to memory that the host assumes is read-only! + If your plug-in has more than one input or output buses then the buffer passed + to the processBlock methods will contain a bundle of all channels of + each bus. Use AudioBusArrangement::getBusBuffer to obtain a audio buffer + for a particular bus. + Note that if you have more outputs than inputs, then only those channels that correspond to an input channel are guaranteed to contain sensible data - e.g. in the case of 2 inputs and 4 outputs, the first two channels contain the input, @@ -163,6 +182,10 @@ public: but you should only read/write from the ones that your filter is supposed to be using. + If your plugin uses buses, then you should use AudioBusArrangement::getBusBuffer() + or AudioBusArrangement::getChannelIndexInProcessBlockBuffer() to find out which + of the input and output channels correspond to which of the buses. + The number of samples in these buffers is NOT guaranteed to be the same for every callback, and may be more or less than the estimated value given to prepareToPlay(). Your code must be able to cope with variable-sized blocks, or you're going to get @@ -186,6 +209,8 @@ public: the UI components register as listeners, and then call sendChangeMessage() inside the processBlock() method to send out an asynchronous message. You could also use the AsyncUpdater class in a similar way. + + @see AudioBusArrangement::getBusBuffer */ virtual void processBlock (AudioBuffer& buffer, MidiBuffer& midiMessages); @@ -212,6 +237,96 @@ public: virtual void processBlockBypassed (AudioBuffer& buffer, MidiBuffer& midiMessages); + //============================================================================== + /** Describes the layout and properties of an audio bus. + Effectively a bus description is a named set of channel types. + @see AudioChannelSet + */ + struct AudioProcessorBus + { + /** Creates a bus from a name and set of channel types. */ + AudioProcessorBus (const String& busName, const AudioChannelSet& channelTypes); + + /** The bus's name. */ + String name; + + /** The set of channel types that the bus contains. */ + AudioChannelSet channels; + }; + + //============================================================================== + /** + Represents a set of input and output buses for an AudioProcessor. + */ + struct AudioBusArrangement + { + /** An array containing the list of input buses that this processor supports. */ + Array inputBuses; + + /** An array containing the list of output buses that this processor supports. */ + Array outputBuses; + + //============================================================================== + /** Returns the position of a bus's channels within the processBlock buffer. + This can be called in processBlock to figure out which channel of the master AudioSampleBuffer + maps onto a specific bus's channel. + */ + int getChannelIndexInProcessBlockBuffer (bool isInput, int busIndex, int channelIndex) const noexcept; + + /** Returns an AudioBuffer containing a set of channel pointers for a specific bus. + This can be called in processBlock to get a buffer containing a sub-group of the master + AudioSampleBuffer which contains all the plugin channels. + */ + template + AudioBuffer getBusBuffer (AudioBuffer& processBlockBuffer, bool isInput, int busIndex) const + { + const int busNumChannels = (isInput ? inputBuses : outputBuses).getReference (busIndex).channels.size(); + const int channelOffset = getChannelIndexInProcessBlockBuffer (isInput, busIndex, 0); + + return AudioBuffer (processBlockBuffer.getArrayOfWritePointers() + channelOffset, + busNumChannels, processBlockBuffer.getNumSamples()); + } + + /** Returns the total number of channels in all the input buses. */ + int getTotalNumInputChannels() const noexcept; + + /** Returns the total number of channels in all the output buses. */ + int getTotalNumOutputChannels() const noexcept; + }; + + /** The processor's bus arrangement. + + Your plugin can modify this either + - in the plugin's constructor + - in the setPreferredBusArrangement() callback + Changing it at other times can result in undefined behaviour. + + The host will negotiate with the plugin over its bus configuration by making calls + to setPreferredBusArrangement(). + + @see setPreferredBusArrangement + */ + AudioBusArrangement busArrangement; + + //============================================================================== + /** Called by the host, this attempts to change the plugin's channel layout on a particular bus. + The base class implementation will perform some basic sanity-checking and then apply the + changes to the processor's busArrangement value. + You may override it and return false if you want to make your plugin smarter about refusing + certain layouts that you don't want to support. Your plug-in may also respond to this call by + changing the channel layout of other buses, for example, if your plug-in requires the same + number of input and output channels. + + Note, that you must not do any heavy allocations or calculations in this callback as it may + be called several hundred times during initialization. If you require any layout specific + allocations then defer these to prepareToPlay callback. + + @returns false if there is no way for the processor to support the given format on the specified bus. + + @see prepareToPlay, busArrangement, AudioBusArrangement::getBusBuffer, getTotalNumInputChannels, getTotalNumOutputChannels + */ + virtual bool setPreferredBusArrangement (bool isInputBus, int busIndex, const AudioChannelSet& preferredSet); + //============================================================================== /** Returns true if the Audio processor supports double precision floating point processing. The default implementation will always return false. @@ -267,6 +382,40 @@ public: */ AudioPlayHead* getPlayHead() const noexcept { return playHead; } + //============================================================================== + /** Returns the total number of input channels. + + This method will return the total number of input channels by accumulating + the number of channels on each input bus. The number of channels of the + buffer passed to your processBlock callback will be equivalent to either + getTotalNumInputChannels or getTotalNumOutputChannels - which ever + is greater. + + Note that getTotalNumInputChannels is equivalent to + getMainBusNumInputChannels if your processor does not have any sidechains + or aux buses. + */ + int getTotalNumInputChannels() const noexcept { return busArrangement.getTotalNumInputChannels(); } + + /** Returns the total number of output channels. + + This method will return the total number of output channels by accumulating + the number of channels on each output bus. The number of channels of the + buffer passed to your processBlock callback will be equivalent to either + getTotalNumInputChannels or getTotalNumOutputChannels - which ever + is greater. + + Note that getTotalNumOutputChannels is equivalent to + getMainBusNumOutputChannels if your processor does not have any sidechains + or aux buses. + */ + int getTotalNumOutputChannels() const noexcept { return busArrangement.getTotalNumOutputChannels(); } + + /** Returns the number of input channels on the main bus. */ + int getMainBusNumInputChannels() const noexcept; + + /** Returns the number of output channels on the main bus. */ + int getMainBusNumOutputChannels() const noexcept; //============================================================================== /** Returns the current sample rate. @@ -288,62 +437,6 @@ public: int getBlockSize() const noexcept { return blockSize; } //============================================================================== - /** Returns the number of input channels that the host will be sending the filter. - - If writing a plugin, your configuration macros should specify the number of - channels that your filter would prefer to have, and this method lets - you know how many the host is actually using. - - Note that this method is only valid during or after the prepareToPlay() - method call. Until that point, the number of channels will be unknown. - */ - int getNumInputChannels() const noexcept { return numInputChannels; } - - /** Returns the number of output channels that the host will be sending the filter. - - If writing a plugin, your configuration macros should specify the number of - channels that your filter would prefer to have, and this method lets - you know how many the host is actually using. - - Note that this method is only valid during or after the prepareToPlay() - method call. Until that point, the number of channels will be unknown. - */ - int getNumOutputChannels() const noexcept { return numOutputChannels; } - - /** Returns a string containing a whitespace-separated list of speaker types - corresponding to each input channel. - For example in a 5.1 arrangement, the string may be "L R C Lfe Ls Rs" - If the speaker arrangement is unknown, the returned string will be empty. - */ - const String& getInputSpeakerArrangement() const noexcept { return inputSpeakerArrangement; } - - /** Returns a string containing a whitespace-separated list of speaker types - corresponding to each output channel. - For example in a 5.1 arrangement, the string may be "L R C Lfe Ls Rs" - If the speaker arrangement is unknown, the returned string will be empty. - */ - const String& getOutputSpeakerArrangement() const noexcept { return outputSpeakerArrangement; } - - //============================================================================== - /** Returns the name of one of the processor's input channels. - - The processor might not supply very useful names for channels, and this might be - something like "1", "2", "left", "right", etc. - */ - virtual const String getInputChannelName (int channelIndex) const = 0; - - /** Returns the name of one of the processor's output channels. - - The processor might not supply very useful names for channels, and this might be - something like "1", "2", "left", "right", etc. - */ - virtual const String getOutputChannelName (int channelIndex) const = 0; - - /** Returns true if the specified channel is part of a stereo pair with its neighbour. */ - virtual bool isInputChannelStereoPair (int index) const = 0; - - /** Returns true if the specified channel is part of a stereo pair with its neighbour. */ - virtual bool isOutputChannelStereoPair (int index) const = 0; /** This returns the number of samples delay that the filter imposes on the audio passing through it. @@ -786,8 +879,18 @@ public: virtual void setPlayHead (AudioPlayHead* newPlayHead); //============================================================================== - /** This is called by the processor to specify its details before being played. */ - void setPlayConfigDetails (int numIns, int numOuts, double sampleRate, int blockSize) noexcept; + /** This is called by the processor to specify its details before being played. Use this + version of the function if you are not interested in any sidechain or aux buses + and do not care about the layout of channels. Otherwise use setRateAndBufferSizeDetails.*/ + void setPlayConfigDetails (int numIns, int numOuts, double sampleRate, int blockSize); + + /** This is called by the processor to specify its details before being played. You + should call this function after having informed the processor about the channel + and bus layouts via setPreferredBusArrangement. + + @see setPreferredBusArrangement + */ + void setRateAndBufferSizeDetails (double sampleRate, int blockSize) noexcept; #if ! JUCE_AUDIO_PROCESSOR_NO_GUI //============================================================================== @@ -795,9 +898,6 @@ public: void editorBeingDeleted (AudioProcessorEditor*) noexcept; #endif - /** Not for public use - this is called to initialise the processor before playing. */ - void setSpeakerArrangement (const String& inputs, const String& outputs); - /** Flags to indicate the type of plugin context in which a processor is being used. */ enum WrapperType { @@ -815,6 +915,36 @@ public: */ WrapperType wrapperType; + //============================================================================== +#ifndef DOXYGEN + /** Deprecated: use getTotalNumInputChannels instead. */ + JUCE_DEPRECATED_WITH_BODY (int getNumInputChannels() const noexcept, { return getTotalNumInputChannels(); }) + JUCE_DEPRECATED_WITH_BODY (int getNumOutputChannels() const noexcept, { return getTotalNumOutputChannels(); }) + + /** Returns a string containing a whitespace-separated list of speaker types + These functions are deprecated: use the methods provided in the AudioChannelSet + class. + */ + JUCE_DEPRECATED_WITH_BODY (const String getInputSpeakerArrangement() const noexcept, { return cachedInputSpeakerArrString; }); + JUCE_DEPRECATED_WITH_BODY (const String getOutputSpeakerArrangement() const noexcept, { return cachedOutputSpeakerArrString; }); + + /** Returns the name of one of the processor's input channels. + + These functions are deprecated: your audio processor can inform the host + on channel layouts and names via the methods in the AudioBusArrangement class. + */ + JUCE_DEPRECATED (virtual const String getInputChannelName (int channelIndex) const); + JUCE_DEPRECATED (virtual const String getOutputChannelName (int channelIndex) const); + + /** Returns true if the specified channel is part of a stereo pair with its neighbour. + + These functions are deprecated: your audio processor should specify the audio + channel pairing information by modifying the busArrangement member variable in + the constructor. */ + JUCE_DEPRECATED (virtual bool isInputChannelStereoPair (int index) const); + JUCE_DEPRECATED (virtual bool isOutputChannelStereoPair (int index) const); +#endif + //============================================================================== /** Helper function that just converts an xml element into a binary blob. @@ -850,11 +980,16 @@ private: Component::SafePointer activeEditor; #endif double sampleRate; - int blockSize, numInputChannels, numOutputChannels, latencySamples; + int blockSize, latencySamples; + #if JUCE_DEBUG + bool textRecursionCheck; + #endif bool suspended, nonRealtime; ProcessingPrecision processingPrecision; CriticalSection callbackLock, listenerLock; - String inputSpeakerArrangement, outputSpeakerArrangement; + + String cachedInputSpeakerArrString; + String cachedOutputSpeakerArrString; OwnedArray managedParameters; AudioProcessorParameter* getParamChecked (int) const noexcept; @@ -864,6 +999,8 @@ private: #endif AudioProcessorListener* getListenerLocked (int) const noexcept; + void disableNonMainBuses (bool isInput); + void updateSpeakerFormatStrings(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessor) }; diff --git a/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp b/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp index bc44fe044..39beaba35 100644 --- a/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp +++ b/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp @@ -386,8 +386,8 @@ private: const int ourRenderingIndex) { AudioProcessor& processor = *node.getProcessor(); - const int numIns = processor.getNumInputChannels(); - const int numOuts = processor.getNumOutputChannels(); + const int numIns = processor.getTotalNumInputChannels(); + const int numOuts = processor.getTotalNumOutputChannels(); const int totalChans = jmax (numIns, numOuts); Array audioChannelsToUse; @@ -767,7 +767,7 @@ private: } else { - for (int i = 0; i < node->getProcessor()->getNumInputChannels(); ++i) + for (int i = 0; i < node->getProcessor()->getTotalNumInputChannels(); ++i) if (i != inputChannelOfIndexToIgnore && graph.getConnectionBetween (nodeId, outputChanIndex, node->nodeId, i) != nullptr) @@ -960,10 +960,14 @@ void AudioProcessorGraph::Node::prepare (const double newSampleRate, const int n processor->setProcessingPrecision (processor->supportsDoublePrecisionProcessing() ? precision : singlePrecision); - processor->setPlayConfigDetails (processor->getNumInputChannels(), - processor->getNumOutputChannels(), + processor->setPlayConfigDetails (processor->getMainBusNumInputChannels(), + processor->getMainBusNumOutputChannels(), newSampleRate, newBlockSize); + // AudioProcessorGraph currently does not support processors with multiple buses + jassert (processor->getMainBusNumInputChannels() == processor->getTotalNumInputChannels() + && processor->getMainBusNumOutputChannels() == processor->getTotalNumOutputChannels()); + processor->prepareToPlay (newSampleRate, newBlockSize); } } @@ -1164,14 +1168,14 @@ bool AudioProcessorGraph::canConnect (const uint32 sourceNodeId, const Node* const source = getNodeForId (sourceNodeId); if (source == nullptr - || (sourceChannelIndex != midiChannelIndex && sourceChannelIndex >= source->processor->getNumOutputChannels()) + || (sourceChannelIndex != midiChannelIndex && sourceChannelIndex >= source->processor->getMainBusNumOutputChannels()) || (sourceChannelIndex == midiChannelIndex && ! source->processor->producesMidi())) return false; const Node* const dest = getNodeForId (destNodeId); if (dest == nullptr - || (destChannelIndex != midiChannelIndex && destChannelIndex >= dest->processor->getNumInputChannels()) + || (destChannelIndex != midiChannelIndex && destChannelIndex >= dest->processor->getMainBusNumInputChannels()) || (destChannelIndex == midiChannelIndex && ! dest->processor->acceptsMidi())) return false; @@ -1249,9 +1253,9 @@ bool AudioProcessorGraph::isConnectionLegal (const Connection* const c) const return source != nullptr && dest != nullptr - && (c->sourceChannelIndex != midiChannelIndex ? isPositiveAndBelow (c->sourceChannelIndex, source->processor->getNumOutputChannels()) + && (c->sourceChannelIndex != midiChannelIndex ? isPositiveAndBelow (c->sourceChannelIndex, source->processor->getMainBusNumOutputChannels()) : source->processor->producesMidi()) - && (c->destChannelIndex != midiChannelIndex ? isPositiveAndBelow (c->destChannelIndex, dest->processor->getNumInputChannels()) + && (c->destChannelIndex != midiChannelIndex ? isPositiveAndBelow (c->destChannelIndex, dest->processor->getMainBusNumInputChannels()) : dest->processor->acceptsMidi()); } @@ -1372,7 +1376,7 @@ void AudioProcessorGraph::handleAsyncUpdate() //============================================================================== void AudioProcessorGraph::prepareToPlay (double /*sampleRate*/, int estimatedSamplesPerBlock) { - audioBuffers->prepareInOutBuffers (jmax (1, getNumOutputChannels()), estimatedSamplesPerBlock); + audioBuffers->prepareInOutBuffers (jmax (1, getTotalNumOutputChannels()), estimatedSamplesPerBlock); currentMidiInputBuffer = nullptr; currentMidiOutputBuffer.clear(); @@ -1410,6 +1414,8 @@ void AudioProcessorGraph::setNonRealtime (bool isProcessingNonRealtime) noexcept { const ScopedLock sl (getCallbackLock()); + AudioProcessor::setNonRealtime (isProcessingNonRealtime); + for (int i = 0; i < nodes.size(); ++i) nodes.getUnchecked(i)->getProcessor()->setNonRealtime (isProcessingNonRealtime); } @@ -1454,18 +1460,6 @@ void AudioProcessorGraph::processAudio (AudioBuffer& buffer, MidiBuff midiMessages.addEvents (currentMidiOutputBuffer, 0, buffer.getNumSamples(), 0); } -const String AudioProcessorGraph::getInputChannelName (int channelIndex) const -{ - return "Input " + String (channelIndex + 1); -} - -const String AudioProcessorGraph::getOutputChannelName (int channelIndex) const -{ - return "Output " + String (channelIndex + 1); -} - -bool AudioProcessorGraph::isInputChannelStereoPair (int) const { return true; } -bool AudioProcessorGraph::isOutputChannelStereoPair (int) const { return true; } bool AudioProcessorGraph::silenceInProducesSilenceOut() const { return false; } double AudioProcessorGraph::getTailLengthSeconds() const { return 0; } bool AudioProcessorGraph::acceptsMidi() const { return true; } @@ -1523,13 +1517,13 @@ void AudioProcessorGraph::AudioGraphIOProcessor::fillInPluginDescription (Plugin d.version = "1.0"; d.isInstrument = false; - d.numInputChannels = getNumInputChannels(); + d.numInputChannels = getMainBusNumInputChannels(); if (type == audioOutputNode && graph != nullptr) - d.numInputChannels = graph->getNumInputChannels(); + d.numInputChannels = graph->getMainBusNumInputChannels(); - d.numOutputChannels = getNumOutputChannels(); + d.numOutputChannels = getMainBusNumOutputChannels(); if (type == audioInputNode && graph != nullptr) - d.numOutputChannels = graph->getNumOutputChannels(); + d.numOutputChannels = graph->getMainBusNumOutputChannels(); } void AudioProcessorGraph::AudioGraphIOProcessor::prepareToPlay (double, int) @@ -1627,40 +1621,6 @@ bool AudioProcessorGraph::AudioGraphIOProcessor::producesMidi() const return type == midiInputNode; } -const String AudioProcessorGraph::AudioGraphIOProcessor::getInputChannelName (int channelIndex) const -{ - switch (type) - { - case audioOutputNode: return "Output " + String (channelIndex + 1); - case midiOutputNode: return "Midi Output"; - default: break; - } - - return String(); -} - -const String AudioProcessorGraph::AudioGraphIOProcessor::getOutputChannelName (int channelIndex) const -{ - switch (type) - { - case audioInputNode: return "Input " + String (channelIndex + 1); - case midiInputNode: return "Midi Input"; - default: break; - } - - return String(); -} - -bool AudioProcessorGraph::AudioGraphIOProcessor::isInputChannelStereoPair (int /*index*/) const -{ - return type == audioInputNode || type == audioOutputNode; -} - -bool AudioProcessorGraph::AudioGraphIOProcessor::isOutputChannelStereoPair (int index) const -{ - return isInputChannelStereoPair (index); -} - bool AudioProcessorGraph::AudioGraphIOProcessor::isInput() const noexcept { return type == audioInputNode || type == midiInputNode; } bool AudioProcessorGraph::AudioGraphIOProcessor::isOutput() const noexcept { return type == audioOutputNode || type == midiOutputNode; } @@ -1685,8 +1645,8 @@ void AudioProcessorGraph::AudioGraphIOProcessor::setParentGraph (AudioProcessorG if (graph != nullptr) { - setPlayConfigDetails (type == audioOutputNode ? graph->getNumOutputChannels() : 0, - type == audioInputNode ? graph->getNumInputChannels() : 0, + setPlayConfigDetails (type == audioOutputNode ? graph->getMainBusNumOutputChannels() : 0, + type == audioInputNode ? graph->getMainBusNumInputChannels() : 0, getSampleRate(), getBlockSize()); diff --git a/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h b/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h index fa27fc799..f4404180c 100644 --- a/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h +++ b/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h @@ -311,10 +311,6 @@ public: void processBlock (AudioBuffer&, MidiBuffer&) override; bool supportsDoublePrecisionProcessing() const override; - const String getInputChannelName (int channelIndex) const override; - const String getOutputChannelName (int channelIndex) const override; - bool isInputChannelStereoPair (int index) const override; - bool isOutputChannelStereoPair (int index) const override; bool silenceInProducesSilenceOut() const override; double getTailLengthSeconds() const override; bool acceptsMidi() const override; @@ -360,11 +356,6 @@ public: void setNonRealtime (bool) noexcept override; void setPlayHead (AudioPlayHead*) override; - const String getInputChannelName (int) const override; - const String getOutputChannelName (int) const override; - bool isInputChannelStereoPair (int) const override; - bool isOutputChannelStereoPair (int) const override; - bool silenceInProducesSilenceOut() const override; double getTailLengthSeconds() const override; bool acceptsMidi() const override; diff --git a/source/modules/juce_core.h b/source/modules/juce_core.h deleted file mode 100644 index 9842637d8..000000000 --- a/source/modules/juce_core.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Carla Juce setup - * Copyright (C) 2013-2014 Filipe Coelho - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or any later version. - * - * This program 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. - * - * For a full copy of the GNU General Public License see the doc/GPL.txt file. - */ - -#ifndef CARLA_JUCE_CORE_H_INCLUDED -#define CARLA_JUCE_CORE_H_INCLUDED - -#include "juce_core/AppConfig.h" -#include "juce_core/juce_core.h" - -#endif // CARLA_JUCE_CORE_H_INCLUDED diff --git a/source/modules/juce_core/AppConfig.h b/source/modules/juce_core/AppConfig.h deleted file mode 100755 index bc4c7c31b..000000000 --- a/source/modules/juce_core/AppConfig.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - ============================================================================== - - Build options for juce_core static library - - ============================================================================== -*/ - -#ifndef CARLA_JUCE_CORE_APPCONFIG_H_INCLUDED -#define CARLA_JUCE_CORE_APPCONFIG_H_INCLUDED - -#include "system/juce_TargetPlatform.h" - -//============================================================================= -/** Config: JUCE_FORCE_DEBUG - - Normally, JUCE_DEBUG is set to 1 or 0 based on compiler and project settings, - but if you define this value, you can override this to force it to be true or false. -*/ -#define JUCE_FORCE_DEBUG 0 - -//============================================================================= -/** Config: JUCE_LOG_ASSERTIONS - - If this flag is enabled, the the jassert and jassertfalse macros will always use Logger::writeToLog() - to write a message when an assertion happens. - - Enabling it will also leave this turned on in release builds. When it's disabled, - however, the jassert and jassertfalse macros will not be compiled in a - release build. - - @see jassert, jassertfalse, Logger -*/ -#define JUCE_LOG_ASSERTIONS 1 - -//============================================================================= -/** Config: JUCE_CHECK_MEMORY_LEAKS - - Enables a memory-leak check for certain objects when the app terminates. See the LeakedObjectDetector - class and the JUCE_LEAK_DETECTOR macro for more details about enabling leak checking for specific classes. -*/ -#ifdef DEBUG - #define JUCE_CHECK_MEMORY_LEAKS 1 -#else - #define JUCE_CHECK_MEMORY_LEAKS 0 -#endif - -//============================================================================= -/** Config: JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES - - In a Visual C++ build, this can be used to stop the required system libs being - automatically added to the link stage. -*/ -#define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES 0 - -/** Config: JUCE_INCLUDE_ZLIB_CODE - This can be used to disable Juce's embedded 3rd-party zlib code. - You might need to tweak this if you're linking to an external zlib library in your app, - but for normal apps, this option should be left alone. - - If you disable this, you might also want to set a value for JUCE_ZLIB_INCLUDE_PATH, to - specify the path where your zlib headers live. -*/ -#define JUCE_INCLUDE_ZLIB_CODE 1 - -/** Config: JUCE_USE_CURL - Enables http/https support via libcurl (Linux only). Enabling this will add an additional - run-time dynmic dependency to libcurl. - - If you disable this then https/ssl support will not be available on linux. -*/ -#define JUCE_USE_CURL 0 - -/* Config: JUCE_CATCH_UNHANDLED_EXCEPTIONS - If enabled, this will add some exception-catching code to forward unhandled exceptions - to your JUCEApplicationBase::unhandledException() callback. -*/ -#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 0 - -// misc -#define JUCE_DISABLE_JUCE_VERSION_PRINTING 1 -#define JUCE_STANDALONE_APPLICATION 0 -#define JUCE_STRING_UTF_TYPE 8 -#define JUCE_USE_VFORK 1 - -// not used/wanted -#define JUCE_USE_XRANDR 0 -#define JUCE_USE_XINERAMA 0 - -#if ! (JUCE_MAC || JUCE_WINDOWS) -# define JUCE_MODAL_LOOPS_PERMITTED 0 -# define JUCE_AUDIO_PROCESSOR_NO_GUI 1 -#endif - -// always enabled -#define JUCE_MODULE_AVAILABLE_juce_audio_basics 1 -#define JUCE_MODULE_AVAILABLE_juce_audio_formats 1 -#define JUCE_MODULE_AVAILABLE_juce_core 1 - -// always disabled -#define JUCE_MODULE_AVAILABLE_juce_audio_plugin_client 0 -#define JUCE_MODULE_AVAILABLE_juce_audio_utils 0 -#define JUCE_MODULE_AVAILABLE_juce_cryptography 0 -#define JUCE_MODULE_AVAILABLE_juce_opengl 0 -#define JUCE_MODULE_AVAILABLE_juce_video 0 - -// conditional -#if JUCE_MAC || JUCE_WINDOWS -# define JUCE_MODULE_AVAILABLE_juce_audio_devices 1 -# define JUCE_MODULE_AVAILABLE_juce_audio_processors 1 -# define JUCE_MODULE_AVAILABLE_juce_data_structures 1 -# define JUCE_MODULE_AVAILABLE_juce_events 1 -# define JUCE_MODULE_AVAILABLE_juce_graphics 1 -# define JUCE_MODULE_AVAILABLE_juce_gui_basics 1 -# define JUCE_MODULE_AVAILABLE_juce_gui_extra 1 -#else -# define JUCE_MODULE_AVAILABLE_juce_audio_devices 0 -# define JUCE_MODULE_AVAILABLE_juce_audio_processors 0 -# define JUCE_MODULE_AVAILABLE_juce_data_structures 0 -# define JUCE_MODULE_AVAILABLE_juce_events 0 -# define JUCE_MODULE_AVAILABLE_juce_graphics 0 -# define JUCE_MODULE_AVAILABLE_juce_gui_basics 0 -# define JUCE_MODULE_AVAILABLE_juce_gui_extra 0 -#endif - -#endif // CARLA_JUCE_CORE_APPCONFIG_H_INCLUDED diff --git a/source/modules/juce_core/Makefile b/source/modules/juce_core/Makefile index a4c0260b0..73099a520 100644 --- a/source/modules/juce_core/Makefile +++ b/source/modules/juce_core/Makefile @@ -10,7 +10,11 @@ include ../Makefile.mk # ---------------------------------------------------------------------------------------------------------------------------- -BUILD_CXX_FLAGS += $(JUCE_CORE_FLAGS) -w +BUILD_CXX_FLAGS += $(JUCE_CORE_FLAGS) -I.. + +ifeq ($(WIN32),true) +BUILD_CXX_FLAGS += -w +endif # ---------------------------------------------------------------------------------------------------------------------------- diff --git a/source/modules/juce_core/containers/juce_Array.h b/source/modules/juce_core/containers/juce_Array.h index 6eb62dd2b..a927d8705 100644 --- a/source/modules/juce_core/containers/juce_Array.h +++ b/source/modules/juce_core/containers/juce_Array.h @@ -365,7 +365,7 @@ public: for (; e != end_; ++e) if (elementToLookFor == *e) - return static_cast (e - data.elements.getData()); + return static_cast (e - data.elements.getData()); return -1; } @@ -762,8 +762,8 @@ public: template int indexOfSorted (ElementComparator& comparator, TargetValueType elementToLookFor) const { - (void) comparator; // if you pass in an object with a static compareElements() method, this - // avoids getting warning messages about the parameter being unused + ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this + // avoids getting warning messages about the parameter being unused const ScopedLockType lock (getLock()); @@ -812,6 +812,28 @@ public: return ElementType(); } + /** Removes an element from the array. + + This will remove the element pointed to by the given iterator, + and move back all the subsequent elements to close the gap. + If the iterator passed in does not point to an element within the + array, behaviour is undefined. + + @param elementToRemove a pointer to the element to remove + @see removeFirstMatchingValue, removeAllInstancesOf, removeRange + */ + void remove (const ElementType* elementToRemove) + { + jassert (elementToRemove != nullptr); + const ScopedLockType lock (getLock()); + + jassert (data.elements != nullptr); + const int indexToRemove = int (elementToRemove - data.elements); + jassert (isPositiveAndBelow (indexToRemove, numUsed)); + + removeInternal (indexToRemove); + } + /** Removes an item from the array. This will remove the first occurrence of the given element from the array. @@ -1094,8 +1116,8 @@ public: const bool retainOrderOfEquivalentItems = false) { const ScopedLockType lock (getLock()); - (void) comparator; // if you pass in an object with a static compareElements() method, this - // avoids getting warning messages about the parameter being unused + ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this + // avoids getting warning messages about the parameter being unused sortArray (comparator, data.elements.getData(), 0, size() - 1, retainOrderOfEquivalentItems); } diff --git a/source/modules/juce_core/containers/juce_ArrayAllocationBase.h b/source/modules/juce_core/containers/juce_ArrayAllocationBase.h index 1a673c988..837e6df9a 100644 --- a/source/modules/juce_core/containers/juce_ArrayAllocationBase.h +++ b/source/modules/juce_core/containers/juce_ArrayAllocationBase.h @@ -60,14 +60,14 @@ public: #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS ArrayAllocationBase (ArrayAllocationBase&& other) noexcept - : elements (static_cast &&> (other.elements)), + : elements (static_cast&&> (other.elements)), numAllocated (other.numAllocated) { } ArrayAllocationBase& operator= (ArrayAllocationBase&& other) noexcept { - elements = static_cast &&> (other.elements); + elements = static_cast&&> (other.elements); numAllocated = other.numAllocated; return *this; } @@ -127,7 +127,7 @@ public: } //============================================================================== - HeapBlock elements; + HeapBlock elements; int numAllocated; private: diff --git a/source/modules/juce_core/containers/juce_ElementComparator.h b/source/modules/juce_core/containers/juce_ElementComparator.h index 41f63ca88..2cca3f510 100644 --- a/source/modules/juce_core/containers/juce_ElementComparator.h +++ b/source/modules/juce_core/containers/juce_ElementComparator.h @@ -128,8 +128,8 @@ static int findInsertIndexInSortedArray (ElementComparator& comparator, { jassert (firstElement <= lastElement); - (void) comparator; // if you pass in an object with a static compareElements() method, this - // avoids getting warning messages about the parameter being unused + ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this + // avoids getting warning messages about the parameter being unused while (firstElement < lastElement) { diff --git a/source/modules/juce_core/containers/juce_ListenerList.h b/source/modules/juce_core/containers/juce_ListenerList.h new file mode 100644 index 000000000..edc08771b --- /dev/null +++ b/source/modules/juce_core/containers/juce_ListenerList.h @@ -0,0 +1,363 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2015 - ROLI Ltd. + + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + +#ifndef JUCE_LISTENERLIST_H_INCLUDED +#define JUCE_LISTENERLIST_H_INCLUDED + + +//============================================================================== +/** + Holds a set of objects and can invoke a member function callback on each object + in the set with a single call. + + Use a ListenerList to manage a set of objects which need a callback, and you + can invoke a member function by simply calling call() or callChecked(). + + E.g. + @code + class MyListenerType + { + public: + void myCallbackMethod (int foo, bool bar); + }; + + ListenerList listeners; + listeners.add (someCallbackObjects...); + + // This will invoke myCallbackMethod (1234, true) on each of the objects + // in the list... + listeners.call (&MyListenerType::myCallbackMethod, 1234, true); + @endcode + + If you add or remove listeners from the list during one of the callbacks - i.e. while + it's in the middle of iterating the listeners, then it's guaranteed that no listeners + will be mistakenly called after they've been removed, but it may mean that some of the + listeners could be called more than once, or not at all, depending on the list's order. + + Sometimes, there's a chance that invoking one of the callbacks might result in the + list itself being deleted while it's still iterating - to survive this situation, you can + use callChecked() instead of call(), passing it a local object to act as a "BailOutChecker". + The BailOutChecker must implement a method of the form "bool shouldBailOut()", and + the list will check this after each callback to determine whether it should abort the + operation. For an example of a bail-out checker, see the Component::BailOutChecker class, + which can be used to check when a Component has been deleted. See also + ListenerList::DummyBailOutChecker, which is a dummy checker that always returns false. +*/ +template > +class ListenerList +{ + // Horrible macros required to support VC7.. + #ifndef DOXYGEN + #if JUCE_VC8_OR_EARLIER + #define LL_TEMPLATE(a) typename P##a, typename Q##a + #define LL_PARAM(a) Q##a& param##a + #else + #define LL_TEMPLATE(a) typename P##a + #define LL_PARAM(a) PARAMETER_TYPE(P##a) param##a + #endif + #endif + +public: + //============================================================================== + /** Creates an empty list. */ + ListenerList() + { + } + + /** Destructor. */ + ~ListenerList() + { + } + + //============================================================================== + /** Adds a listener to the list. + A listener can only be added once, so if the listener is already in the list, + this method has no effect. + @see remove + */ + void add (ListenerClass* const listenerToAdd) + { + // Listeners can't be null pointers! + jassert (listenerToAdd != nullptr); + + if (listenerToAdd != nullptr) + listeners.addIfNotAlreadyThere (listenerToAdd); + } + + /** Removes a listener from the list. + If the listener wasn't in the list, this has no effect. + */ + void remove (ListenerClass* const listenerToRemove) + { + // Listeners can't be null pointers! + jassert (listenerToRemove != nullptr); + + listeners.removeFirstMatchingValue (listenerToRemove); + } + + /** Returns the number of registered listeners. */ + int size() const noexcept + { + return listeners.size(); + } + + /** Returns true if any listeners are registered. */ + bool isEmpty() const noexcept + { + return listeners.size() == 0; + } + + /** Clears the list. */ + void clear() + { + listeners.clear(); + } + + /** Returns true if the specified listener has been added to the list. */ + bool contains (ListenerClass* const listener) const noexcept + { + return listeners.contains (listener); + } + + //============================================================================== + /** Calls a member function on each listener in the list, with no parameters. */ + void call (void (ListenerClass::*callbackFunction) ()) + { + callChecked (static_cast (DummyBailOutChecker()), callbackFunction); + } + + /** Calls a member function on each listener in the list, with no parameters and a bail-out-checker. + See the class description for info about writing a bail-out checker. */ + template + void callChecked (const BailOutCheckerType& bailOutChecker, + void (ListenerClass::*callbackFunction) ()) + { + for (Iterator iter (*this); iter.next (bailOutChecker);) + (iter.getListener()->*callbackFunction) (); + } + + //============================================================================== + /** Calls a member function on each listener in the list, with 1 parameter. */ + template + void call (void (ListenerClass::*callbackFunction) (P1), LL_PARAM(1)) + { + for (Iterator iter (*this); iter.next();) + (iter.getListener()->*callbackFunction) (param1); + } + + /** Calls a member function on each listener in the list, with one parameter and a bail-out-checker. + See the class description for info about writing a bail-out checker. */ + template + void callChecked (const BailOutCheckerType& bailOutChecker, + void (ListenerClass::*callbackFunction) (P1), + LL_PARAM(1)) + { + for (Iterator iter (*this); iter.next (bailOutChecker);) + (iter.getListener()->*callbackFunction) (param1); + } + + //============================================================================== + /** Calls a member function on each listener in the list, with 2 parameters. */ + template + void call (void (ListenerClass::*callbackFunction) (P1, P2), + LL_PARAM(1), LL_PARAM(2)) + { + for (Iterator iter (*this); iter.next();) + (iter.getListener()->*callbackFunction) (param1, param2); + } + + /** Calls a member function on each listener in the list, with 2 parameters and a bail-out-checker. + See the class description for info about writing a bail-out checker. */ + template + void callChecked (const BailOutCheckerType& bailOutChecker, + void (ListenerClass::*callbackFunction) (P1, P2), + LL_PARAM(1), LL_PARAM(2)) + { + for (Iterator iter (*this); iter.next (bailOutChecker);) + (iter.getListener()->*callbackFunction) (param1, param2); + } + + //============================================================================== + /** Calls a member function on each listener in the list, with 3 parameters. */ + template + void call (void (ListenerClass::*callbackFunction) (P1, P2, P3), + LL_PARAM(1), LL_PARAM(2), LL_PARAM(3)) + { + for (Iterator iter (*this); iter.next();) + (iter.getListener()->*callbackFunction) (param1, param2, param3); + } + + /** Calls a member function on each listener in the list, with 3 parameters and a bail-out-checker. + See the class description for info about writing a bail-out checker. */ + template + void callChecked (const BailOutCheckerType& bailOutChecker, + void (ListenerClass::*callbackFunction) (P1, P2, P3), + LL_PARAM(1), LL_PARAM(2), LL_PARAM(3)) + { + for (Iterator iter (*this); iter.next (bailOutChecker);) + (iter.getListener()->*callbackFunction) (param1, param2, param3); + } + + //============================================================================== + /** Calls a member function on each listener in the list, with 4 parameters. */ + template + void call (void (ListenerClass::*callbackFunction) (P1, P2, P3, P4), + LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4)) + { + for (Iterator iter (*this); iter.next();) + (iter.getListener()->*callbackFunction) (param1, param2, param3, param4); + } + + /** Calls a member function on each listener in the list, with 4 parameters and a bail-out-checker. + See the class description for info about writing a bail-out checker. */ + template + void callChecked (const BailOutCheckerType& bailOutChecker, + void (ListenerClass::*callbackFunction) (P1, P2, P3, P4), + LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4)) + { + for (Iterator iter (*this); iter.next (bailOutChecker);) + (iter.getListener()->*callbackFunction) (param1, param2, param3, param4); + } + + //============================================================================== + /** Calls a member function on each listener in the list, with 5 parameters. */ + template + void call (void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5), + LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5)) + { + for (Iterator iter (*this); iter.next();) + (iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5); + } + + /** Calls a member function on each listener in the list, with 5 parameters and a bail-out-checker. + See the class description for info about writing a bail-out checker. */ + template + void callChecked (const BailOutCheckerType& bailOutChecker, + void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5), + LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5)) + { + for (Iterator iter (*this); iter.next (bailOutChecker);) + (iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5); + } + + //============================================================================== + /** Calls a member function on each listener in the list, with 5 parameters. */ + template + void call (void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5, P6), + LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5), LL_PARAM(6)) + { + for (Iterator iter (*this); iter.next();) + (iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5, param6); + } + + /** Calls a member function on each listener in the list, with 5 parameters and a bail-out-checker. + See the class description for info about writing a bail-out checker. */ + template + void callChecked (const BailOutCheckerType& bailOutChecker, + void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5, P6), + LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5), LL_PARAM(6)) + { + for (Iterator iter (*this); iter.next (bailOutChecker);) + (iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5, param6); + } + + + //============================================================================== + /** A dummy bail-out checker that always returns false. + See the ListenerList notes for more info about bail-out checkers. + */ + class DummyBailOutChecker + { + public: + inline bool shouldBailOut() const noexcept { return false; } + }; + + //============================================================================== + /** Iterates the listeners in a ListenerList. */ + template + class Iterator + { + public: + //============================================================================== + Iterator (const ListType& listToIterate) noexcept + : list (listToIterate), index (listToIterate.size()) + {} + + ~Iterator() noexcept {} + + //============================================================================== + bool next() noexcept + { + if (index <= 0) + return false; + + const int listSize = list.size(); + + if (--index < listSize) + return true; + + index = listSize - 1; + return index >= 0; + } + + bool next (const BailOutCheckerType& bailOutChecker) noexcept + { + return (! bailOutChecker.shouldBailOut()) && next(); + } + + typename ListType::ListenerType* getListener() const noexcept + { + return list.getListeners().getUnchecked (index); + } + + //============================================================================== + private: + const ListType& list; + int index; + + JUCE_DECLARE_NON_COPYABLE (Iterator) + }; + + typedef ListenerList ThisType; + typedef ListenerClass ListenerType; + + const ArrayType& getListeners() const noexcept { return listeners; } + +private: + //============================================================================== + ArrayType listeners; + + JUCE_DECLARE_NON_COPYABLE (ListenerList) + + #undef LL_TEMPLATE + #undef LL_PARAM +}; + + +#endif // JUCE_LISTENERLIST_H_INCLUDED diff --git a/source/modules/juce_core/containers/juce_NamedValueSet.cpp b/source/modules/juce_core/containers/juce_NamedValueSet.cpp index 68ba5f88a..94ea828be 100644 --- a/source/modules/juce_core/containers/juce_NamedValueSet.cpp +++ b/source/modules/juce_core/containers/juce_NamedValueSet.cpp @@ -79,7 +79,7 @@ NamedValueSet& NamedValueSet::operator= (const NamedValueSet& other) #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS NamedValueSet::NamedValueSet (NamedValueSet&& other) noexcept - : values (static_cast &&> (other.values)) + : values (static_cast&&> (other.values)) { } diff --git a/source/modules/juce_core/containers/juce_OwnedArray.h b/source/modules/juce_core/containers/juce_OwnedArray.h index aa2f2fbaa..31078f619 100644 --- a/source/modules/juce_core/containers/juce_OwnedArray.h +++ b/source/modules/juce_core/containers/juce_OwnedArray.h @@ -74,7 +74,7 @@ public: #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS OwnedArray (OwnedArray&& other) noexcept - : data (static_cast &&> (other.data)), + : data (static_cast&&> (other.data)), numUsed (other.numUsed) { other.numUsed = 0; @@ -85,7 +85,7 @@ public: const ScopedLockType lock (getLock()); deleteAllObjects(); - data = static_cast &&> (other.data); + data = static_cast&&> (other.data); numUsed = other.numUsed; other.numUsed = 0; return *this; @@ -239,7 +239,7 @@ public: for (; e != end_; ++e) if (objectToLookFor == *e) - return static_cast (e - data.elements.getData()); + return static_cast (e - data.elements.getData()); return -1; } @@ -523,8 +523,8 @@ public: template int addSorted (ElementComparator& comparator, ObjectClass* const newObject) noexcept { - (void) comparator; // if you pass in an object with a static compareElements() method, this - // avoids getting warning messages about the parameter being unused + ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this + // avoids getting warning messages about the parameter being unused const ScopedLockType lock (getLock()); const int index = findInsertIndexInSortedArray (comparator, data.elements.getData(), newObject, 0, numUsed); insert (index, newObject); @@ -546,7 +546,7 @@ public: template int indexOfSorted (ElementComparator& comparator, const ObjectClass* const objectToLookFor) const noexcept { - (void) comparator; + ignoreUnused (comparator); const ScopedLockType lock (getLock()); int s = 0, e = numUsed; @@ -854,8 +854,8 @@ public: void sort (ElementComparator& comparator, bool retainOrderOfEquivalentItems = false) const noexcept { - (void) comparator; // if you pass in an object with a static compareElements() method, this - // avoids getting warning messages about the parameter being unused + ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this + // avoids getting warning messages about the parameter being unused const ScopedLockType lock (getLock()); sortArray (comparator, data.elements.getData(), 0, size() - 1, retainOrderOfEquivalentItems); diff --git a/source/modules/juce_core/containers/juce_ReferenceCountedArray.h b/source/modules/juce_core/containers/juce_ReferenceCountedArray.h index 12a8925ce..1b4668eda 100644 --- a/source/modules/juce_core/containers/juce_ReferenceCountedArray.h +++ b/source/modules/juce_core/containers/juce_ReferenceCountedArray.h @@ -281,7 +281,7 @@ public: while (e != endPointer) { if (objectToLookFor == *e) - return static_cast (e - data.elements.getData()); + return static_cast (e - data.elements.getData()); ++e; } @@ -518,7 +518,7 @@ public: int indexOfSorted (ElementComparator& comparator, const ObjectClass* const objectToLookFor) const noexcept { - (void) comparator; + ignoreUnused (comparator); const ScopedLockType lock (getLock()); int s = 0, e = numUsed; @@ -835,8 +835,8 @@ public: void sort (ElementComparator& comparator, const bool retainOrderOfEquivalentItems = false) const noexcept { - (void) comparator; // if you pass in an object with a static compareElements() method, this - // avoids getting warning messages about the parameter being unused + ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this + // avoids getting warning messages about the parameter being unused const ScopedLockType lock (getLock()); sortArray (comparator, data.elements.getData(), 0, size() - 1, retainOrderOfEquivalentItems); diff --git a/source/modules/juce_core/files/juce_File.cpp b/source/modules/juce_core/files/juce_File.cpp index a8c2c690f..9737fa8aa 100644 --- a/source/modules/juce_core/files/juce_File.cpp +++ b/source/modules/juce_core/files/juce_File.cpp @@ -57,13 +57,13 @@ File& File::operator= (const File& other) #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS File::File (File&& other) noexcept - : fullPath (static_cast (other.fullPath)) + : fullPath (static_cast (other.fullPath)) { } File& File::operator= (File&& other) noexcept { - fullPath = static_cast (other.fullPath); + fullPath = static_cast (other.fullPath); return *this; } #endif diff --git a/source/modules/juce_core/files/juce_File.h b/source/modules/juce_core/files/juce_File.h index f54096cc9..8e729a5ce 100644 --- a/source/modules/juce_core/files/juce_File.h +++ b/source/modules/juce_core/files/juce_File.h @@ -430,7 +430,8 @@ public: If it already exists or is a directory, this method will do nothing. - @returns true if the file has been created (or if it already existed). + @returns a result to indicate whether the file was created successfully, + or an error message if it failed. @see createDirectory */ Result create() const; diff --git a/source/modules/juce_core/files/juce_FileOutputStream.h b/source/modules/juce_core/files/juce_FileOutputStream.h index 7d1520cf5..83c8f12a7 100644 --- a/source/modules/juce_core/files/juce_FileOutputStream.h +++ b/source/modules/juce_core/files/juce_FileOutputStream.h @@ -101,7 +101,7 @@ private: Result status; int64 currentPosition; size_t bufferSize, bytesInBuffer; - HeapBlock buffer; + HeapBlock buffer; void openHandle(); void closeHandle(); diff --git a/source/modules/juce_core/files/juce_TemporaryFile.h b/source/modules/juce_core/files/juce_TemporaryFile.h index 0aad9c90b..64ea9903c 100644 --- a/source/modules/juce_core/files/juce_TemporaryFile.h +++ b/source/modules/juce_core/files/juce_TemporaryFile.h @@ -48,7 +48,7 @@ TemporaryFile temp (myTargetFile); // create a stream to the temporary file, and write some data to it... - ScopedPointer out (temp.getFile().createOutputStream()); + ScopedPointer out (temp.getFile().createOutputStream()); if (out != nullptr) { diff --git a/source/modules/juce_core/javascript/juce_Javascript.cpp b/source/modules/juce_core/javascript/juce_Javascript.cpp index 761cbbe2f..3c8c0114d 100644 --- a/source/modules/juce_core/javascript/juce_Javascript.cpp +++ b/source/modules/juce_core/javascript/juce_Javascript.cpp @@ -1466,7 +1466,7 @@ struct JavascriptEngine::RootObject : public DynamicObject } static Identifier getClassName() { static const Identifier i ("Object"); return i; } - static var dump (Args a) { DBG (JSON::toString (a.thisObject)); (void) a; return var::undefined(); } + static var dump (Args a) { DBG (JSON::toString (a.thisObject)); ignoreUnused (a); return var::undefined(); } static var cloneFn (Args a) { return a.thisObject.clone(); } }; @@ -1560,7 +1560,6 @@ struct JavascriptEngine::RootObject : public DynamicObject setMethod ("random", Math_random); setMethod ("randInt", Math_randInt); setMethod ("min", Math_min); setMethod ("max", Math_max); setMethod ("range", Math_range); setMethod ("sign", Math_sign); - setMethod ("PI", Math_pi); setMethod ("E", Math_e); setMethod ("toDegrees", Math_toDegrees); setMethod ("toRadians", Math_toRadians); setMethod ("sin", Math_sin); setMethod ("asin", Math_asin); setMethod ("sinh", Math_sinh); setMethod ("asinh", Math_asinh); @@ -1572,10 +1571,11 @@ struct JavascriptEngine::RootObject : public DynamicObject setMethod ("exp", Math_exp); setMethod ("pow", Math_pow); setMethod ("sqr", Math_sqr); setMethod ("sqrt", Math_sqrt); setMethod ("ceil", Math_ceil); setMethod ("floor", Math_floor); + + setProperty ("PI", double_Pi); + setProperty ("E", exp (1.0)); } - static var Math_pi (Args) { return double_Pi; } - static var Math_e (Args) { return exp (1.0); } static var Math_random (Args) { return Random::getSystemRandom().nextDouble(); } static var Math_randInt (Args a) { return Random::getSystemRandom().nextInt (Range (getInt (a, 0), getInt (a, 1))); } static var Math_abs (Args a) { return isInt (a, 0) ? var (std::abs (getInt (a, 0))) : var (std::abs (getDouble (a, 0))); } diff --git a/source/modules/juce_core/juce_core.cpp b/source/modules/juce_core/juce_core.cpp index 38f03dbf5..c43d0cd8e 100644 --- a/source/modules/juce_core/juce_core.cpp +++ b/source/modules/juce_core/juce_core.cpp @@ -26,7 +26,7 @@ ============================================================================== */ -#if defined (JUCE_CORE_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE +#ifdef JUCE_CORE_H_INCLUDED /* 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 @@ -35,11 +35,6 @@ #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 "native/juce_BasicNativeHeaders.h" #include "juce_core.h" @@ -114,6 +109,8 @@ #include #endif +#undef check + //============================================================================== #ifndef JUCE_STANDALONE_APPLICATION JUCE_COMPILER_WARNING ("Please re-save your Introjucer project with the latest Introjucer version to avoid this warning") diff --git a/source/modules/juce_core/juce_core.h b/source/modules/juce_core/juce_core.h index 9f30f12f4..e9beac3d3 100644 --- a/source/modules/juce_core/juce_core.h +++ b/source/modules/juce_core/juce_core.h @@ -29,25 +29,7 @@ #ifndef JUCE_CORE_H_INCLUDED #define JUCE_CORE_H_INCLUDED -#ifndef JUCE_MODULE_AVAILABLE_juce_core - /* If you fail to make sure that all your compile units are building JUCE with the same set of - option flags, then there's a risk that different compile units will treat the classes as having - different memory layouts, leading to very nasty memory corruption errors when they all get - linked together. That's why it's best to always include the Introjucer-generated AppConfig.h - file before any juce headers. - - Note that if you do have an AppConfig.h file and hit this warning, it means that it doesn't - contain the JUCE_MODULE_AVAILABLE_xxx flags, which are necessary for some inter-module - functionality to work correctly. In that case, you should either rebuild your AppConfig.h with - the latest introjucer, or fix it manually to contain these flags. - */ - #ifdef _MSC_VER - #pragma message ("Have you included your AppConfig.h file before including the JUCE headers?") - #else - #warning "Have you included your AppConfig.h file before including the JUCE headers?" - #endif -#endif - +//============================================================================== #ifdef _MSC_VER #pragma warning (push) // Disable warnings for long class names, padding, and undefined preprocessor definitions. @@ -57,7 +39,6 @@ #endif #endif -//============================================================================== #include "system/juce_TargetPlatform.h" //============================================================================= @@ -168,7 +149,7 @@ class FileOutputStream; class XmlElement; class JSONFormatter; -extern JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger(); +extern JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept; extern JUCE_API void JUCE_CALLTYPE logAssertion (const char* file, int line) noexcept; #include "memory/juce_Memory.h" @@ -211,6 +192,7 @@ extern JUCE_API void JUCE_CALLTYPE logAssertion (const char* file, int line) noe #include "containers/juce_ArrayAllocationBase.h" #include "containers/juce_Array.h" #include "containers/juce_LinkedListPointer.h" +#include "containers/juce_ListenerList.h" #include "containers/juce_OwnedArray.h" #include "containers/juce_ReferenceCountedArray.h" #include "containers/juce_ScopedValueSetter.h" diff --git a/source/modules/juce_core/maths/juce_BigInteger.cpp b/source/modules/juce_core/maths/juce_BigInteger.cpp index 84eabe3fd..f03710156 100644 --- a/source/modules/juce_core/maths/juce_BigInteger.cpp +++ b/source/modules/juce_core/maths/juce_BigInteger.cpp @@ -87,7 +87,7 @@ BigInteger::BigInteger (const BigInteger& other) #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS BigInteger::BigInteger (BigInteger&& other) noexcept - : values (static_cast &&> (other.values)), + : values (static_cast&&> (other.values)), numValues (other.numValues), highestBit (other.highestBit), negative (other.negative) @@ -96,7 +96,7 @@ BigInteger::BigInteger (BigInteger&& other) noexcept BigInteger& BigInteger::operator= (BigInteger&& other) noexcept { - values = static_cast &&> (other.values); + values = static_cast&&> (other.values); numValues = other.numValues; highestBit = other.highestBit; negative = other.negative; diff --git a/source/modules/juce_core/maths/juce_Expression.cpp b/source/modules/juce_core/maths/juce_Expression.cpp index 71535b879..97a15a50f 100644 --- a/source/modules/juce_core/maths/juce_Expression.cpp +++ b/source/modules/juce_core/maths/juce_Expression.cpp @@ -397,7 +397,7 @@ struct Expression::Helpers JUCE_DECLARE_NON_COPYABLE (SymbolRenamingVisitor) }; - SymbolTerm* getSymbol() const { return static_cast (left.get()); } + SymbolTerm* getSymbol() const { return static_cast (left.get()); } JUCE_DECLARE_NON_COPYABLE (DotOperator) }; @@ -427,7 +427,7 @@ struct Expression::Helpers TermPtr createTermToEvaluateInput (const Scope& scope, const Term* t, double overallTarget, Term* topLevelTerm) const { - (void) t; + ignoreUnused (t); jassert (t == input); const Term* const dest = findDestinationFor (topLevelTerm, this); @@ -958,13 +958,13 @@ Expression& Expression::operator= (const Expression& other) #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Expression::Expression (Expression&& other) noexcept - : term (static_cast &&> (other.term)) + : term (static_cast&&> (other.term)) { } Expression& Expression::operator= (Expression&& other) noexcept { - term = static_cast &&> (other.term); + term = static_cast&&> (other.term); return *this; } #endif diff --git a/source/modules/juce_core/maths/juce_MathsFunctions.h b/source/modules/juce_core/maths/juce_MathsFunctions.h index faa85e024..9d9a142cc 100644 --- a/source/modules/juce_core/maths/juce_MathsFunctions.h +++ b/source/modules/juce_core/maths/juce_MathsFunctions.h @@ -215,7 +215,7 @@ void findMinAndMax (const Type* values, int numValues, Type& lowest, Type& highe @param valueToConstrain the value to try to return @returns the closest value to valueToConstrain which lies between lowerLimit and upperLimit (inclusive) - @see jlimit0To, jmin, jmax + @see jmin, jmax, jmap */ template Type jlimit (const Type lowerLimit, @@ -245,7 +245,7 @@ template <> inline bool isPositiveAndBelow (const int valueToTest, const int upperLimit) noexcept { jassert (upperLimit >= 0); // makes no sense to call this if the upper limit is itself below zero.. - return static_cast (valueToTest) < static_cast (upperLimit); + return static_cast (valueToTest) < static_cast (upperLimit); } /** Returns true if a value is at least zero, and also less than or equal to a specified upper limit. @@ -264,7 +264,7 @@ template <> inline bool isPositiveAndNotGreaterThan (const int valueToTest, const int upperLimit) noexcept { jassert (upperLimit >= 0); // makes no sense to call this if the upper limit is itself below zero.. - return static_cast (valueToTest) <= static_cast (upperLimit); + return static_cast (valueToTest) <= static_cast (upperLimit); } //============================================================================== @@ -299,7 +299,7 @@ void ignoreUnused (const Type1&, const Type2&, const Type3&, const Type4&) noexc template int numElementsInArray (Type (&array)[N]) { - (void) array; // (required to avoid a spurious warning in MS compilers) + ignoreUnused (array); (void) sizeof (0[array]); // This line should cause an error if you pass an object with a user-defined subscript operator return N; } diff --git a/source/modules/juce_core/memory/juce_Atomic.h b/source/modules/juce_core/memory/juce_Atomic.h index bbf9d67b9..970d4d38e 100644 --- a/source/modules/juce_core/memory/juce_Atomic.h +++ b/source/modules/juce_core/memory/juce_Atomic.h @@ -178,7 +178,7 @@ private: template inline PointerType* negateValue (PointerType* n) noexcept { - return reinterpret_cast (-reinterpret_cast (n)); + return reinterpret_cast (-reinterpret_cast (n)); } }; @@ -190,18 +190,12 @@ private: #if JUCE_MAC && (JUCE_PPC || __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2)) #define JUCE_ATOMICS_MAC_LEGACY 1 // Older OSX builds using gcc4.1 or earlier - #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 - #define JUCE_MAC_ATOMICS_VOLATILE - #else - #define JUCE_MAC_ATOMICS_VOLATILE volatile - #endif - #if JUCE_PPC // None of these atomics are available for PPC or for iOS 3.1 or earlier!! - template static Type OSAtomicAdd64Barrier (Type b, JUCE_MAC_ATOMICS_VOLATILE Type* a) noexcept { jassertfalse; return *a += b; } - template static Type OSAtomicIncrement64Barrier (JUCE_MAC_ATOMICS_VOLATILE Type* a) noexcept { jassertfalse; return ++*a; } - template static Type OSAtomicDecrement64Barrier (JUCE_MAC_ATOMICS_VOLATILE Type* a) noexcept { jassertfalse; return --*a; } - template static bool OSAtomicCompareAndSwap64Barrier (Type old, Type newValue, JUCE_MAC_ATOMICS_VOLATILE Type* value) noexcept + template static Type OSAtomicAdd64Barrier (Type b, volatile Type* a) noexcept { jassertfalse; return *a += b; } + template static Type OSAtomicIncrement64Barrier (volatile Type* a) noexcept { jassertfalse; return ++*a; } + template static Type OSAtomicDecrement64Barrier (volatile Type* a) noexcept { jassertfalse; return --*a; } + template static bool OSAtomicCompareAndSwap64Barrier (Type old, Type newValue, volatile Type* value) noexcept { jassertfalse; if (old == *value) { *value = newValue; return true; } return false; } #define JUCE_64BIT_ATOMICS_UNAVAILABLE 1 #endif @@ -264,27 +258,27 @@ private: template struct WindowsInterlockedHelpersBase { - static inline Type exchange(volatile Type* value, Type other) noexcept + static inline Type exchange (volatile Type* value, Type other) noexcept { return castFrom (juce_InterlockedExchange (reinterpret_cast (value), castTo (other))); } - static inline Type add(volatile Type* value, Type other) noexcept + static inline Type add (volatile Type* value, Type other) noexcept { return castFrom (juce_InterlockedExchangeAdd (reinterpret_cast (value), castTo (other)) + castTo (other)); } - static inline Type inc(volatile Type* value) noexcept + static inline Type inc (volatile Type* value) noexcept { return castFrom (juce_InterlockedIncrement (reinterpret_cast (value))); } - static inline Type dec(volatile Type* value) noexcept + static inline Type dec (volatile Type* value) noexcept { return castFrom (juce_InterlockedDecrement (reinterpret_cast (value))); } - static inline Type cmp(volatile Type* value, Type other, Type comparand) noexcept + static inline Type cmp (volatile Type* value, Type other, Type comparand) noexcept { return castFrom (juce_InterlockedCompareExchange (reinterpret_cast (value), castTo (other), castTo (comparand))); } @@ -296,27 +290,27 @@ private: template struct WindowsInterlockedHelpersBase { - static inline Type exchange(volatile Type* value, Type other) noexcept + static inline Type exchange (volatile Type* value, Type other) noexcept { return castFrom (juce_InterlockedExchange64 (reinterpret_cast (value), castTo (other))); } - static inline Type add(volatile Type* value, Type other) noexcept + static inline Type add (volatile Type* value, Type other) noexcept { return castFrom (juce_InterlockedExchangeAdd64 (reinterpret_cast (value), castTo (other)) + castTo (other)); } - static inline Type inc(volatile Type* value) noexcept + static inline Type inc (volatile Type* value) noexcept { return castFrom (juce_InterlockedIncrement64 (reinterpret_cast (value))); } - static inline Type dec(volatile Type* value) noexcept + static inline Type dec (volatile Type* value) noexcept { return castFrom (juce_InterlockedDecrement64 (reinterpret_cast (value))); } - static inline Type cmp(volatile Type* value, Type other, Type comparand) noexcept + static inline Type cmp (volatile Type* value, Type other, Type comparand) noexcept { return castFrom (juce_InterlockedCompareExchange64 (reinterpret_cast (value), castTo (other), castTo (comparand))); } @@ -340,8 +334,8 @@ template inline Type Atomic::get() const noexcept { #if JUCE_ATOMICS_MAC_LEGACY - return sizeof (Type) == 4 ? castFrom32Bit ((int32) OSAtomicAdd32Barrier ((int32_t) 0, (JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value)) - : castFrom64Bit ((int64) OSAtomicAdd64Barrier ((int64_t) 0, (JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value)); + return sizeof (Type) == 4 ? castFrom32Bit ((int32) OSAtomicAdd32Barrier ((int32_t) 0, (volatile int32_t*) &value)) + : castFrom64Bit ((int64) OSAtomicAdd64Barrier ((int64_t) 0, (volatile int64_t*) &value)); #elif JUCE_ATOMICS_WINDOWS return WindowsInterlockedHelpers::add (const_cast (&value), (Type) 0); #elif JUCE_ATOMICS_GCC @@ -366,8 +360,8 @@ template inline Type Atomic::operator+= (const Type amountToAdd) noexcept { #if JUCE_ATOMICS_MAC_LEGACY - return sizeof (Type) == 4 ? (Type) OSAtomicAdd32Barrier ((int32_t) castTo32Bit (amountToAdd), (JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) - : (Type) OSAtomicAdd64Barrier ((int64_t) amountToAdd, (JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); + return sizeof (Type) == 4 ? (Type) OSAtomicAdd32Barrier ((int32_t) castTo32Bit (amountToAdd), (volatile int32_t*) &value) + : (Type) OSAtomicAdd64Barrier ((int64_t) amountToAdd, (volatile int64_t*) &value); #elif JUCE_ATOMICS_WINDOWS return WindowsInterlockedHelpers::add (&value, amountToAdd); #elif JUCE_ATOMICS_GCC @@ -385,8 +379,8 @@ template inline Type Atomic::operator++() noexcept { #if JUCE_ATOMICS_MAC_LEGACY - return sizeof (Type) == 4 ? (Type) OSAtomicIncrement32Barrier ((JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) - : (Type) OSAtomicIncrement64Barrier ((JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); + return sizeof (Type) == 4 ? (Type) OSAtomicIncrement32Barrier ((volatile int32_t*) &value) + : (Type) OSAtomicIncrement64Barrier ((volatile int64_t*) &value); #elif JUCE_ATOMICS_WINDOWS return WindowsInterlockedHelpers::inc (&value); #elif JUCE_ATOMICS_GCC @@ -399,8 +393,8 @@ template inline Type Atomic::operator--() noexcept { #if JUCE_ATOMICS_MAC_LEGACY - return sizeof (Type) == 4 ? (Type) OSAtomicDecrement32Barrier ((JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) - : (Type) OSAtomicDecrement64Barrier ((JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); + return sizeof (Type) == 4 ? (Type) OSAtomicDecrement32Barrier ((volatile int32_t*) &value) + : (Type) OSAtomicDecrement64Barrier ((volatile int64_t*) &value); #elif JUCE_ATOMICS_WINDOWS return WindowsInterlockedHelpers::dec (&value); #elif JUCE_ATOMICS_GCC @@ -413,8 +407,8 @@ template inline bool Atomic::compareAndSetBool (const Type newValue, const Type valueToCompare) noexcept { #if JUCE_ATOMICS_MAC_LEGACY - return sizeof (Type) == 4 ? OSAtomicCompareAndSwap32Barrier ((int32_t) castTo32Bit (valueToCompare), (int32_t) castTo32Bit (newValue), (JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) - : OSAtomicCompareAndSwap64Barrier ((int64_t) castTo64Bit (valueToCompare), (int64_t) castTo64Bit (newValue), (JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); + return sizeof (Type) == 4 ? OSAtomicCompareAndSwap32Barrier ((int32_t) castTo32Bit (valueToCompare), (int32_t) castTo32Bit (newValue), (volatile int32_t*) &value) + : OSAtomicCompareAndSwap64Barrier ((int64_t) castTo64Bit (valueToCompare), (int64_t) castTo64Bit (newValue), (volatile int64_t*) &value); #elif JUCE_ATOMICS_WINDOWS return compareAndSetValue (newValue, valueToCompare) == valueToCompare; #elif JUCE_ATOMICS_GCC diff --git a/source/modules/juce_core/memory/juce_HeapBlock.h b/source/modules/juce_core/memory/juce_HeapBlock.h index bf0ff83b8..5045c4a59 100644 --- a/source/modules/juce_core/memory/juce_HeapBlock.h +++ b/source/modules/juce_core/memory/juce_HeapBlock.h @@ -33,10 +33,10 @@ namespace HeapBlockHelper { template - struct ThrowOnFail { static void check (void*) {} }; + struct ThrowOnFail { static void checkPointer (void*) {} }; template<> - struct ThrowOnFail { static void check (void* data) { if (data == nullptr) throw std::bad_alloc(); } }; + struct ThrowOnFail { static void checkPointer (void* data) { if (data == nullptr) throw std::bad_alloc(); } }; } #endif @@ -298,7 +298,7 @@ private: #if JUCE_EXCEPTIONS_DISABLED jassert (data != nullptr); // without exceptions, you'll need to find a better way to handle this failure case. #else - HeapBlockHelper::ThrowOnFail::check (data); + HeapBlockHelper::ThrowOnFail::checkPointer (data); #endif } diff --git a/source/modules/juce_core/memory/juce_ReferenceCountedObject.h b/source/modules/juce_core/memory/juce_ReferenceCountedObject.h index 3dac3c46c..fca745eba 100644 --- a/source/modules/juce_core/memory/juce_ReferenceCountedObject.h +++ b/source/modules/juce_core/memory/juce_ReferenceCountedObject.h @@ -260,7 +260,7 @@ public: */ template ReferenceCountedObjectPtr (const ReferenceCountedObjectPtr& other) noexcept - : referencedObject (static_cast (other.get())) + : referencedObject (static_cast (other.get())) { incIfNotNull (referencedObject); } diff --git a/source/modules/juce_core/memory/juce_WeakReference.h b/source/modules/juce_core/memory/juce_WeakReference.h index ff23d994c..ca3dd1fb2 100644 --- a/source/modules/juce_core/memory/juce_WeakReference.h +++ b/source/modules/juce_core/memory/juce_WeakReference.h @@ -98,8 +98,8 @@ public: WeakReference& operator= (ObjectType* const newObject) { holder = getRef (newObject); return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - WeakReference (WeakReference&& other) noexcept : holder (static_cast (other.holder)) {} - WeakReference& operator= (WeakReference&& other) noexcept { holder = static_cast (other.holder); return *this; } + WeakReference (WeakReference&& other) noexcept : holder (static_cast (other.holder)) {} + WeakReference& operator= (WeakReference&& other) noexcept { holder = static_cast (other.holder); return *this; } #endif /** Returns the object that this pointer refers to, or null if the object no longer exists. */ diff --git a/source/modules/juce_core/misc/juce_Result.cpp b/source/modules/juce_core/misc/juce_Result.cpp index 0999531a9..e2a1c1301 100644 --- a/source/modules/juce_core/misc/juce_Result.cpp +++ b/source/modules/juce_core/misc/juce_Result.cpp @@ -46,13 +46,13 @@ Result& Result::operator= (const Result& other) #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Result::Result (Result&& other) noexcept - : errorMessage (static_cast (other.errorMessage)) + : errorMessage (static_cast (other.errorMessage)) { } Result& Result::operator= (Result&& other) noexcept { - errorMessage = static_cast (other.errorMessage); + errorMessage = static_cast (other.errorMessage); return *this; } #endif diff --git a/source/modules/juce_core/misc/juce_WindowsRegistry.h b/source/modules/juce_core/misc/juce_WindowsRegistry.h index 987d87c7b..2840e1892 100644 --- a/source/modules/juce_core/misc/juce_WindowsRegistry.h +++ b/source/modules/juce_core/misc/juce_WindowsRegistry.h @@ -60,7 +60,7 @@ public: e.g. "HKEY_CURRENT_USER\Software\foo\bar" */ static String JUCE_CALLTYPE getValue (const String& regValuePath, - const String& defaultValue = String::empty, + const String& defaultValue = String(), WoW64Mode mode = WoW64_Default); /** Reads a binary block from the registry. @@ -128,7 +128,7 @@ public: WoW64Mode mode = WoW64_Default); // DEPRECATED: use the other methods with a WoW64Mode parameter of WoW64_64bit instead. - JUCE_DEPRECATED (static String getValueWow64 (const String&, const String& defaultValue = String::empty)); + JUCE_DEPRECATED (static String getValueWow64 (const String&, const String& defaultValue = String())); JUCE_DEPRECATED (static bool valueExistsWow64 (const String&)); JUCE_DEPRECATED (static bool keyExistsWow64 (const String&)); diff --git a/source/modules/juce_core/native/juce_android_Files.cpp b/source/modules/juce_core/native/juce_android_Files.cpp index 591c4dc19..a3442149e 100644 --- a/source/modules/juce_core/native/juce_android_Files.cpp +++ b/source/modules/juce_core/native/juce_android_Files.cpp @@ -43,7 +43,7 @@ bool File::isOnRemovableDrive() const String File::getVersion() const { - return String::empty; + return String(); } File File::getSpecialLocation (const SpecialLocationType type) diff --git a/source/modules/juce_core/native/juce_android_Threads.cpp b/source/modules/juce_core/native/juce_android_Threads.cpp index 2cca156bb..585b97a7e 100644 --- a/source/modules/juce_core/native/juce_android_Threads.cpp +++ b/source/modules/juce_core/native/juce_android_Threads.cpp @@ -62,16 +62,11 @@ JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority prior) pthread_setschedparam (pthread_self(), policy, ¶m); } -JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() +JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept { return false; } -JUCE_API bool JUCE_CALLTYPE Process::isRunningUnderDebugger() -{ - return juce_isRunningUnderDebugger(); -} - JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() {} JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() {} diff --git a/source/modules/juce_core/native/juce_linux_Network.cpp b/source/modules/juce_core/native/juce_linux_Network.cpp index 30a05823b..a05518c9a 100644 --- a/source/modules/juce_core/native/juce_linux_Network.cpp +++ b/source/modules/juce_core/native/juce_linux_Network.cpp @@ -453,7 +453,7 @@ private: const int numToSend = jmin (1024, (int) (requestHeader.getSize() - totalHeaderSent)); - if (send (socketHandle, static_cast (requestHeader.getData()) + totalHeaderSent, (size_t) numToSend, 0) != numToSend) + if (send (socketHandle, static_cast (requestHeader.getData()) + totalHeaderSent, (size_t) numToSend, 0) != numToSend) return false; totalHeaderSent += (size_t) numToSend; diff --git a/source/modules/juce_core/native/juce_linux_SystemStats.cpp b/source/modules/juce_core/native/juce_linux_SystemStats.cpp index 9ac6901b9..85062bf2e 100644 --- a/source/modules/juce_core/native/juce_linux_SystemStats.cpp +++ b/source/modules/juce_core/native/juce_linux_SystemStats.cpp @@ -197,7 +197,7 @@ bool Time::setSystemTimeToThisTime() const return settimeofday (&t, 0) == 0; } -JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() +JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept { #if JUCE_BSD return false; diff --git a/source/modules/juce_core/native/juce_linux_Threads.cpp b/source/modules/juce_core/native/juce_linux_Threads.cpp index b9d3d4506..465acee8d 100644 --- a/source/modules/juce_core/native/juce_linux_Threads.cpp +++ b/source/modules/juce_core/native/juce_linux_Threads.cpp @@ -52,11 +52,6 @@ JUCE_API void JUCE_CALLTYPE Process::setPriority (const ProcessPriority prior) pthread_setschedparam (pthread_self(), policy, ¶m); } -JUCE_API bool JUCE_CALLTYPE Process::isRunningUnderDebugger() -{ - return juce_isRunningUnderDebugger(); -} - static bool swapUserAndEffectiveUser() { int result1 = setreuid (geteuid(), getuid()); diff --git a/source/modules/juce_core/native/juce_mac_ClangBugWorkaround.h b/source/modules/juce_core/native/juce_mac_ClangBugWorkaround.h new file mode 100644 index 000000000..8f478e049 --- /dev/null +++ b/source/modules/juce_core/native/juce_mac_ClangBugWorkaround.h @@ -0,0 +1,36 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2015 - ROLI Ltd. + + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + + +// This hack is a workaround for a bug (?) in Apple's 10.11 SDK headers +// which cause some configurations of Clang to throw out a spurious error.. +#if JUCE_PROJUCER_LIVE_BUILD && (defined (__APPLE_CPP__) || defined(__APPLE_CC__)) + #include + #undef CF_OPTIONS + #define CF_OPTIONS(_type, _name) _type _name; enum +#endif diff --git a/source/modules/juce_core/native/juce_mac_Files.mm b/source/modules/juce_core/native/juce_mac_Files.mm index 035cc43ed..6a5c86f61 100644 --- a/source/modules/juce_core/native/juce_mac_Files.mm +++ b/source/modules/juce_core/native/juce_mac_Files.mm @@ -276,12 +276,7 @@ String File::getVersion() const //============================================================================== static NSString* getFileLink (const String& path) { - #if JUCE_IOS || (defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) return [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath: juceStringToNS (path) error: nil]; - #else - // (the cast here avoids a deprecation warning) - return [((id) [NSFileManager defaultManager]) pathContentOfSymbolicLinkAtPath: juceStringToNS (path)]; - #endif } bool File::isSymbolicLink() const @@ -408,7 +403,7 @@ bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String& filenameAsURL = [NSURL fileURLWithPath: fileNameAsNS]; #if JUCE_IOS - (void) parameters; + ignoreUnused (parameters); return [[UIApplication sharedApplication] openURL: filenameAsURL]; #else NSWorkspace* workspace = [NSWorkspace sharedWorkspace]; @@ -461,13 +456,7 @@ OSType File::getMacOSType() const { JUCE_AUTORELEASEPOOL { - #if JUCE_IOS || (defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) NSDictionary* fileDict = [[NSFileManager defaultManager] attributesOfItemAtPath: juceStringToNS (getFullPathName()) error: nil]; - #else - // (the cast here avoids a deprecation warning) - NSDictionary* fileDict = [((id) [NSFileManager defaultManager]) fileAttributesAtPath: juceStringToNS (getFullPathName()) traverseLink: NO]; - #endif - return [fileDict fileHFSTypeCode]; } } diff --git a/source/modules/juce_core/native/juce_mac_Network.mm b/source/modules/juce_core/native/juce_mac_Network.mm index 287bba98d..f14adc744 100644 --- a/source/modules/juce_core/native/juce_mac_Network.mm +++ b/source/modules/juce_core/native/juce_mac_Network.mm @@ -64,10 +64,7 @@ bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& targetEmailA const StringArray& filesToAttach) { #if JUCE_IOS - (void) targetEmailAddress; - (void) emailSubject; - (void) bodyText; - (void) filesToAttach; + ignoreUnused (targetEmailAddress, emailSubject, bodyText, filesToAttach); //xxx probably need to use MFMailComposeViewController jassertfalse; @@ -234,7 +231,7 @@ public: void didFailWithError (NSError* error) { - DBG (nsStringToJuce ([error description])); (void) error; + DBG (nsStringToJuce ([error description])); ignoreUnused (error); hasFailed = true; initialised = true; signalThreadShouldExit(); diff --git a/source/modules/juce_core/native/juce_mac_SystemStats.mm b/source/modules/juce_core/native/juce_mac_SystemStats.mm index c1b437194..0767a7606 100644 --- a/source/modules/juce_core/native/juce_mac_SystemStats.mm +++ b/source/modules/juce_core/native/juce_mac_SystemStats.mm @@ -84,11 +84,7 @@ void CPUInformation::initialise() noexcept hasAVX = (c & (1u << 28)) != 0; #endif - #if JUCE_IOS || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) numCpus = (int) [[NSProcessInfo processInfo] activeProcessorCount]; - #else - numCpus = (int) MPProcessors(); - #endif } #if JUCE_MAC @@ -182,7 +178,7 @@ String SystemStats::getCpuVendor() SystemStatsHelpers::doCPUID (dummy, vendor[0], vendor[2], vendor[1], 0); - return String (reinterpret_cast (vendor), 12); + return String (reinterpret_cast (vendor), 12); #else return String(); #endif diff --git a/source/modules/juce_core/native/juce_mac_Threads.mm b/source/modules/juce_core/native/juce_mac_Threads.mm index 8150a15e0..b8b793b63 100644 --- a/source/modules/juce_core/native/juce_mac_Threads.mm +++ b/source/modules/juce_core/native/juce_mac_Threads.mm @@ -75,23 +75,11 @@ JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority) } //============================================================================== -JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() +JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept { - static char testResult = 0; - - if (testResult == 0) - { - struct kinfo_proc info; - int m[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() }; - size_t sz = sizeof (info); - sysctl (m, 4, &info, &sz, 0, 0); - testResult = ((info.kp_proc.p_flag & P_TRACED) != 0) ? 1 : -1; - } - - return testResult > 0; -} - -JUCE_API bool JUCE_CALLTYPE Process::isRunningUnderDebugger() -{ - return juce_isRunningUnderDebugger(); + struct kinfo_proc info; + int m[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() }; + size_t sz = sizeof (info); + sysctl (m, 4, &info, &sz, 0, 0); + return (info.kp_proc.p_flag & P_TRACED) != 0; } diff --git a/source/modules/juce_core/native/juce_osx_ObjCHelpers.h b/source/modules/juce_core/native/juce_osx_ObjCHelpers.h index cc7c56a9d..afd1d0228 100644 --- a/source/modules/juce_core/native/juce_osx_ObjCHelpers.h +++ b/source/modules/juce_core/native/juce_osx_ObjCHelpers.h @@ -60,10 +60,10 @@ namespace template static NSRect makeNSRect (const RectangleType& r) noexcept { - return NSMakeRect (static_cast (r.getX()), - static_cast (r.getY()), - static_cast (r.getWidth()), - static_cast (r.getHeight())); + return NSMakeRect (static_cast (r.getX()), + static_cast (r.getY()), + static_cast (r.getWidth()), + static_cast (r.getHeight())); } // These hacks are a workaround for newer Xcode builds which by default prevent calls to these objc functions.. @@ -115,14 +115,14 @@ struct ObjCClass void addIvar (const char* name) { BOOL b = class_addIvar (cls, name, sizeof (Type), (uint8_t) rint (log2 (sizeof (Type))), @encode (Type)); - jassert (b); (void) b; + jassert (b); ignoreUnused (b); } template void addMethod (SEL selector, FunctionType callbackFn, const char* signature) { BOOL b = class_addMethod (cls, selector, (IMP) callbackFn, signature); - jassert (b); (void) b; + jassert (b); ignoreUnused (b); } template @@ -146,7 +146,7 @@ struct ObjCClass void addProtocol (Protocol* protocol) { BOOL b = class_addProtocol (cls, protocol); - jassert (b); (void) b; + jassert (b); ignoreUnused (b); } #if JUCE_MAC @@ -162,7 +162,7 @@ struct ObjCClass { void* v = nullptr; object_getInstanceVariable (self, name, &v); - return static_cast (v); + return static_cast (v); } Class cls; diff --git a/source/modules/juce_core/native/juce_posix_NamedPipe.cpp b/source/modules/juce_core/native/juce_posix_NamedPipe.cpp index 45a9f8a58..9fb3fe588 100644 --- a/source/modules/juce_core/native/juce_posix_NamedPipe.cpp +++ b/source/modules/juce_core/native/juce_posix_NamedPipe.cpp @@ -191,7 +191,7 @@ void NamedPipe::close() char buffer[1] = { 0 }; ssize_t done = ::write (pimpl->pipeIn, buffer, 1); - (void) done; + ignoreUnused (done); ScopedWriteLock sl (lock); pimpl = nullptr; @@ -224,11 +224,11 @@ bool NamedPipe::openInternal (const String& pipeName, const bool createPipe, boo int NamedPipe::read (void* destBuffer, int maxBytesToRead, int timeOutMilliseconds) { ScopedReadLock sl (lock); - return pimpl != nullptr ? pimpl->read (static_cast (destBuffer), maxBytesToRead, timeOutMilliseconds) : -1; + return pimpl != nullptr ? pimpl->read (static_cast (destBuffer), maxBytesToRead, timeOutMilliseconds) : -1; } int NamedPipe::write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds) { ScopedReadLock sl (lock); - return pimpl != nullptr ? pimpl->write (static_cast (sourceBuffer), numBytesToWrite, timeOutMilliseconds) : -1; + return pimpl != nullptr ? pimpl->write (static_cast (sourceBuffer), numBytesToWrite, timeOutMilliseconds) : -1; } diff --git a/source/modules/juce_core/native/juce_posix_SharedCode.h b/source/modules/juce_core/native/juce_posix_SharedCode.h index edfcf5d79..1b70e1688 100644 --- a/source/modules/juce_core/native/juce_posix_SharedCode.h +++ b/source/modules/juce_core/native/juce_posix_SharedCode.h @@ -706,7 +706,7 @@ void juce_runSystemCommand (const String&); void juce_runSystemCommand (const String& command) { int result = system (command.toUTF8()); - (void) result; + ignoreUnused (result); } String juce_getOutputFromCommand (const String&); @@ -914,7 +914,7 @@ void Thread::killThread() void JUCE_CALLTYPE Thread::setCurrentThreadName (const String& name) { - #if JUCE_IOS || (JUCE_MAC && defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) + #if JUCE_IOS || JUCE_MAC JUCE_AUTORELEASEPOOL { [[NSThread currentThread] setName: juceStringToNS (name)]; @@ -1323,7 +1323,7 @@ private: THREAD_TIME_CONSTRAINT_POLICY_COUNT) == KERN_SUCCESS; #else - (void) periodMs; + ignoreUnused (periodMs); struct sched_param param; param.sched_priority = sched_get_priority_max (SCHED_RR); return pthread_setschedparam (thread, SCHED_RR, ¶m) == 0; diff --git a/source/modules/juce_core/native/juce_win32_ComSmartPtr.h b/source/modules/juce_core/native/juce_win32_ComSmartPtr.h index 10d5d204c..380e9d843 100644 --- a/source/modules/juce_core/native/juce_win32_ComSmartPtr.h +++ b/source/modules/juce_core/native/juce_win32_ComSmartPtr.h @@ -145,7 +145,7 @@ protected: template JUCE_COMRESULT castToType (void** result) { - this->AddRef(); *result = dynamic_cast (this); return S_OK; + this->AddRef(); *result = dynamic_cast (this); return S_OK; } }; diff --git a/source/modules/juce_core/native/juce_win32_Files.cpp b/source/modules/juce_core/native/juce_win32_Files.cpp index 81a0bca64..ebf480f75 100644 --- a/source/modules/juce_core/native/juce_win32_Files.cpp +++ b/source/modules/juce_core/native/juce_win32_Files.cpp @@ -65,7 +65,7 @@ namespace WindowsFileHelpers path.copyToUTF16 (pathCopy, numBytes); if (PathStripToRoot (pathCopy)) - path = static_cast (pathCopy); + path = static_cast (pathCopy); return path; } @@ -120,6 +120,7 @@ namespace WindowsFileHelpers const juce_wchar File::separator = '\\'; const String File::separatorString ("\\"); +void* getUser32Function (const char*); //============================================================================== bool File::exists() const @@ -643,7 +644,13 @@ bool File::isShortcut() const File File::getLinkedTarget() const { -#if JUCE_MSVC + #if JUCE_WINDOWS + typedef DWORD (WINAPI* GetFinalPathNameByHandleFunc) (HANDLE, LPTSTR, DWORD, DWORD); + + static GetFinalPathNameByHandleFunc getFinalPathNameByHandle + = (GetFinalPathNameByHandleFunc) getUser32Function ("GetFinalPathNameByHandle"); + + if (getFinalPathNameByHandle != nullptr) { HANDLE h = CreateFile (getFullPathName().toWideCharPointer(), GENERIC_READ, FILE_SHARE_READ, nullptr, @@ -651,16 +658,11 @@ File File::getLinkedTarget() const if (h != INVALID_HANDLE_VALUE) { - DWORD requiredSize = ::GetFinalPathNameByHandleW (h, nullptr, 0, FILE_NAME_NORMALIZED); - - if (requiredSize > 0) + if (DWORD requiredSize = getFinalPathNameByHandle (h, nullptr, 0, 0 /* FILE_NAME_NORMALIZED */)) { - HeapBlock buffer (requiredSize + 2); - buffer.clear (requiredSize + 2); - - requiredSize = ::GetFinalPathNameByHandleW (h, buffer, requiredSize, FILE_NAME_NORMALIZED); + HeapBlock buffer (requiredSize + 2, true); - if (requiredSize > 0) + if (getFinalPathNameByHandle (h, buffer, requiredSize, 0 /* FILE_NAME_NORMALIZED */) > 0) { CloseHandle (h); @@ -677,7 +679,7 @@ File File::getLinkedTarget() const CloseHandle (h); } } -#endif + #endif File result (*this); String p (getFullPathName()); @@ -796,14 +798,8 @@ bool DirectoryIterator::NativeIterator::next (String& filenameFound, //============================================================================== bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String& parameters) { - HINSTANCE hInstance = 0; - - JUCE_TRY - { - hInstance = ShellExecute (0, 0, fileName.toWideCharPointer(), - parameters.toWideCharPointer(), 0, SW_SHOWDEFAULT); - } - JUCE_CATCH_ALL + HINSTANCE hInstance = ShellExecute (0, 0, fileName.toWideCharPointer(), + parameters.toWideCharPointer(), 0, SW_SHOWDEFAULT); return hInstance > (HINSTANCE) 32; } diff --git a/source/modules/juce_core/native/juce_win32_Network.cpp b/source/modules/juce_core/native/juce_win32_Network.cpp index 437a6e50a..dc80f7733 100644 --- a/source/modules/juce_core/native/juce_win32_Network.cpp +++ b/source/modules/juce_core/native/juce_win32_Network.cpp @@ -505,7 +505,7 @@ bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& targetEmailA message.nRecipCount = 1; message.lpRecips = &recip; - HeapBlock files; + HeapBlock files; files.calloc ((size_t) filesToAttach.size()); message.nFileCount = (ULONG) filesToAttach.size(); diff --git a/source/modules/juce_core/native/juce_win32_Registry.cpp b/source/modules/juce_core/native/juce_win32_Registry.cpp index 164f2e24c..8615f4325 100644 --- a/source/modules/juce_core/native/juce_win32_Registry.cpp +++ b/source/modules/juce_core/native/juce_win32_Registry.cpp @@ -70,7 +70,7 @@ struct RegistryKeyWrapper return key.key != 0 && RegSetValueEx (key.key, key.wideCharValueName, 0, type, - reinterpret_cast (data), + reinterpret_cast (data), (DWORD) dataSize) == ERROR_SUCCESS; } @@ -107,7 +107,7 @@ struct RegistryKeyWrapper MemoryBlock buffer; switch (getBinaryValue (regValuePath, buffer, wow64Flags)) { - case REG_SZ: return static_cast (buffer.getData()); + case REG_SZ: return static_cast (buffer.getData()); case REG_DWORD: return String ((int) *reinterpret_cast (buffer.getData())); default: break; } diff --git a/source/modules/juce_core/native/juce_win32_SystemStats.cpp b/source/modules/juce_core/native/juce_win32_SystemStats.cpp index 0db49030a..fee1b764e 100644 --- a/source/modules/juce_core/native/juce_win32_SystemStats.cpp +++ b/source/modules/juce_core/native/juce_win32_SystemStats.cpp @@ -275,7 +275,7 @@ public: #if JUCE_WIN32_TIMER_PERIOD > 0 const MMRESULT res = timeBeginPeriod (JUCE_WIN32_TIMER_PERIOD); - (void) res; + ignoreUnused (res); jassert (res == TIMERR_NOERROR); #endif diff --git a/source/modules/juce_core/native/juce_win32_Threads.cpp b/source/modules/juce_core/native/juce_win32_Threads.cpp index 9856fd307..062c18d39 100644 --- a/source/modules/juce_core/native/juce_win32_Threads.cpp +++ b/source/modules/juce_core/native/juce_win32_Threads.cpp @@ -154,7 +154,7 @@ void JUCE_CALLTYPE Thread::setCurrentThreadName (const String& name) __except (EXCEPTION_CONTINUE_EXECUTION) {} #else - (void) name; + ignoreUnused (name); #endif } @@ -259,16 +259,11 @@ void JUCE_CALLTYPE Process::setPriority (ProcessPriority prior) } } -JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() +JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept { return IsDebuggerPresent() != FALSE; } -bool JUCE_CALLTYPE Process::isRunningUnderDebugger() -{ - return juce_isRunningUnderDebugger(); -} - static void* currentModuleHandle = nullptr; void* JUCE_CALLTYPE Process::getCurrentModuleInstanceHandle() noexcept @@ -314,27 +309,17 @@ bool juce_isRunningInWine() bool DynamicLibrary::open (const String& name) { close(); - - JUCE_TRY - { - handle = LoadLibrary (name.toWideCharPointer()); - } - JUCE_CATCH_ALL - + handle = LoadLibrary (name.toWideCharPointer()); return handle != nullptr; } void DynamicLibrary::close() { - JUCE_TRY + if (handle != nullptr) { - if (handle != nullptr) - { - FreeLibrary ((HMODULE) handle); - handle = nullptr; - } + FreeLibrary ((HMODULE) handle); + handle = nullptr; } - JUCE_CATCH_ALL } void* DynamicLibrary::getFunction (const String& functionName) noexcept diff --git a/source/modules/juce_core/streams/juce_BufferedInputStream.cpp b/source/modules/juce_core/streams/juce_BufferedInputStream.cpp index ea91f64a5..addc441bb 100644 --- a/source/modules/juce_core/streams/juce_BufferedInputStream.cpp +++ b/source/modules/juce_core/streams/juce_BufferedInputStream.cpp @@ -158,7 +158,7 @@ int BufferedInputStream::read (void* destBuffer, int maxBytesToRead) maxBytesToRead -= bytesAvailable; bytesRead += bytesAvailable; position += bytesAvailable; - destBuffer = static_cast (destBuffer) + bytesAvailable; + destBuffer = static_cast (destBuffer) + bytesAvailable; } const int64 oldLastReadPos = lastReadPos; diff --git a/source/modules/juce_core/streams/juce_BufferedInputStream.h b/source/modules/juce_core/streams/juce_BufferedInputStream.h index e0e74a29b..c7eccb92c 100644 --- a/source/modules/juce_core/streams/juce_BufferedInputStream.h +++ b/source/modules/juce_core/streams/juce_BufferedInputStream.h @@ -83,7 +83,7 @@ private: OptionalScopedPointer source; int bufferSize; int64 position, lastReadPos, bufferStart, bufferOverlap; - HeapBlock buffer; + HeapBlock buffer; void ensureBuffered(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BufferedInputStream) diff --git a/source/modules/juce_core/system/juce_PlatformDefs.h b/source/modules/juce_core/system/juce_PlatformDefs.h index 54ab399a6..2ff33bcdf 100644 --- a/source/modules/juce_core/system/juce_PlatformDefs.h +++ b/source/modules/juce_core/system/juce_PlatformDefs.h @@ -55,9 +55,9 @@ // Debugging and assertion macros #if JUCE_LOG_ASSERTIONS || JUCE_DEBUG - #define juce_LogCurrentAssertion juce::logAssertion (__FILE__, __LINE__); + #define JUCE_LOG_CURRENT_ASSERTION juce::logAssertion (__FILE__, __LINE__); #else - #define juce_LogCurrentAssertion + #define JUCE_LOG_CURRENT_ASSERTION #endif //============================================================================== @@ -67,20 +67,20 @@ crash or not, depending on the platform. @see jassert() */ - #define juce_breakDebugger { ::kill (0, SIGTRAP); } + #define JUCE_BREAK_IN_DEBUGGER { ::kill (0, SIGTRAP); } #elif JUCE_USE_MSVC_INTRINSICS #ifndef __INTEL_COMPILER #pragma intrinsic (__debugbreak) #endif - #define juce_breakDebugger { __debugbreak(); } + #define JUCE_BREAK_IN_DEBUGGER { __debugbreak(); } #elif JUCE_GCC || JUCE_MAC #if JUCE_NO_INLINE_ASM - #define juce_breakDebugger { } + #define JUCE_BREAK_IN_DEBUGGER { } #else - #define juce_breakDebugger { asm ("int $3"); } + #define JUCE_BREAK_IN_DEBUGGER { asm ("int $3"); } #endif #else - #define juce_breakDebugger { __asm int 3 } + #define JUCE_BREAK_IN_DEBUGGER { __asm int 3 } #endif #if JUCE_CLANG && defined (__has_feature) && ! defined (JUCE_ANALYZER_NORETURN) @@ -96,7 +96,7 @@ //============================================================================== #if JUCE_MSVC && ! DOXYGEN - #define MACRO_WITH_FORCED_SEMICOLON(x) \ + #define JUCE_BLOCK_WITH_FORCED_SEMICOLON(x) \ __pragma(warning(push)) \ __pragma(warning(disable:4127)) \ do { x } while (false) \ @@ -105,23 +105,29 @@ /** This is the good old C++ trick for creating a macro that forces the user to put a semicolon after it when they use it. */ - #define MACRO_WITH_FORCED_SEMICOLON(x) do { x } while (false) + #define JUCE_BLOCK_WITH_FORCED_SEMICOLON(x) do { x } while (false) #endif //============================================================================== #if JUCE_DEBUG || DOXYGEN /** Writes a string to the standard error stream. - This is only compiled in a debug build. + Note that as well as a single string, you can use this to write multiple items + as a stream, e.g. + @code + DBG ("foo = " << foo << "bar = " << bar); + @endcode + The macro is only enabled in a debug build, so be careful not to use it with expressions + that have important side-effects! @see Logger::outputDebugString */ - #define DBG(dbgtext) MACRO_WITH_FORCED_SEMICOLON (juce::String tempDbgBuf; tempDbgBuf << dbgtext; juce::Logger::outputDebugString (tempDbgBuf);) + #define DBG(textToWrite) JUCE_BLOCK_WITH_FORCED_SEMICOLON (juce::String tempDbgBuf; tempDbgBuf << textToWrite; juce::Logger::outputDebugString (tempDbgBuf);) //============================================================================== /** This will always cause an assertion failure. It is only compiled in a debug build, (unless JUCE_LOG_ASSERTIONS is enabled for your build). @see jassert */ - #define jassertfalse MACRO_WITH_FORCED_SEMICOLON (juce_LogCurrentAssertion; if (juce::juce_isRunningUnderDebugger()) juce_breakDebugger; JUCE_ANALYZER_NORETURN) + #define jassertfalse JUCE_BLOCK_WITH_FORCED_SEMICOLON (JUCE_LOG_CURRENT_ASSERTION; if (juce::juce_isRunningUnderDebugger()) JUCE_BREAK_IN_DEBUGGER; JUCE_ANALYZER_NORETURN) //============================================================================== /** Platform-independent assertion macro. @@ -131,19 +137,19 @@ correct behaviour of your program! @see jassertfalse */ - #define jassert(expression) MACRO_WITH_FORCED_SEMICOLON (if (! (expression)) jassertfalse;) + #define jassert(expression) JUCE_BLOCK_WITH_FORCED_SEMICOLON (if (! (expression)) jassertfalse;) #else //============================================================================== // If debugging is disabled, these dummy debug and assertion macros are used.. - #define DBG(dbgtext) - #define jassertfalse MACRO_WITH_FORCED_SEMICOLON (juce_LogCurrentAssertion) + #define DBG(textToWrite) + #define jassertfalse JUCE_BLOCK_WITH_FORCED_SEMICOLON (JUCE_LOG_CURRENT_ASSERTION) #if JUCE_LOG_ASSERTIONS - #define jassert(expression) MACRO_WITH_FORCED_SEMICOLON (if (! (expression)) jassertfalse;) + #define jassert(expression) JUCE_BLOCK_WITH_FORCED_SEMICOLON (if (! (expression)) jassertfalse;) #else - #define jassert(a) MACRO_WITH_FORCED_SEMICOLON ( ; ) + #define jassert(expression) JUCE_BLOCK_WITH_FORCED_SEMICOLON ( ; ) #endif #endif @@ -251,40 +257,6 @@ #endif -//============================================================================== -#if JUCE_CATCH_UNHANDLED_EXCEPTIONS - - #define JUCE_TRY try - - #define JUCE_CATCH_ALL catch (...) {} - #define JUCE_CATCH_ALL_ASSERT catch (...) { jassertfalse; } - - #if ! JUCE_MODULE_AVAILABLE_juce_gui_basics - #define JUCE_CATCH_EXCEPTION JUCE_CATCH_ALL - #else - /** Used in try-catch blocks, this macro will send exceptions to the JUCEApplicationBase - object so they can be logged by the application if it wants to. - */ - #define JUCE_CATCH_EXCEPTION \ - catch (const std::exception& e) \ - { \ - juce::JUCEApplicationBase::sendUnhandledException (&e, __FILE__, __LINE__); \ - } \ - catch (...) \ - { \ - juce::JUCEApplicationBase::sendUnhandledException (nullptr, __FILE__, __LINE__); \ - } - #endif - -#else - - #define JUCE_TRY - #define JUCE_CATCH_EXCEPTION - #define JUCE_CATCH_ALL - #define JUCE_CATCH_ALL_ASSERT - -#endif - //============================================================================== #if JUCE_DEBUG || DOXYGEN /** A platform-independent way of forcing an inline function. diff --git a/source/modules/juce_core/system/juce_StandardHeader.h b/source/modules/juce_core/system/juce_StandardHeader.h index bf5d2de87..dd5e86e88 100644 --- a/source/modules/juce_core/system/juce_StandardHeader.h +++ b/source/modules/juce_core/system/juce_StandardHeader.h @@ -35,8 +35,8 @@ See also SystemStats::getJUCEVersion() for a string version. */ #define JUCE_MAJOR_VERSION 4 -#define JUCE_MINOR_VERSION 0 -#define JUCE_BUILDNUMBER 2 +#define JUCE_MINOR_VERSION 1 +#define JUCE_BUILDNUMBER 0 /** Current Juce version number. @@ -114,7 +114,6 @@ #endif // undef symbols that are sometimes set by misguided 3rd-party headers.. -#undef check #undef TYPE_BOOL #undef max #undef min diff --git a/source/modules/juce_core/system/juce_SystemStats.h b/source/modules/juce_core/system/juce_SystemStats.h index 540bf73ea..9cbd187e0 100644 --- a/source/modules/juce_core/system/juce_SystemStats.h +++ b/source/modules/juce_core/system/juce_SystemStats.h @@ -64,6 +64,7 @@ public: MacOSX_10_8 = MacOSX | 8, MacOSX_10_9 = MacOSX | 9, MacOSX_10_10 = MacOSX | 10, + MacOSX_10_11 = MacOSX | 11, Win2000 = Windows | 1, WinXP = Windows | 2, diff --git a/source/modules/juce_core/system/juce_TargetPlatform.h b/source/modules/juce_core/system/juce_TargetPlatform.h index 6f900ec79..6dd013085 100644 --- a/source/modules/juce_core/system/juce_TargetPlatform.h +++ b/source/modules/juce_core/system/juce_TargetPlatform.h @@ -42,6 +42,15 @@ - Either JUCE_GCC or JUCE_MSVC */ +//============================================================================== +#ifdef JUCE_APP_CONFIG_HEADER + #include JUCE_APP_CONFIG_HEADER +#else + // 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" +#endif + //============================================================================== #if (defined (_WIN32) || defined (_WIN64)) #define JUCE_WIN32 1 @@ -55,16 +64,10 @@ #define Point CarbonDummyPointName // (workaround to avoid definition of "Point" by old Carbon headers) #define Component CarbonDummyCompName #include // (needed to find out what platform we're using) + #include "../native/juce_mac_ClangBugWorkaround.h" #undef Point #undef Component - #if JUCE_PROJUCER_LIVE_BUILD - // This hack is a workaround for a bug (?) in Apple's 10.11 SDK headers - // which cause some configurations of Clang to throw out an error.. - #undef CF_OPTIONS - #define CF_OPTIONS(_type, _name) _type _name; enum - #endif - #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR #define JUCE_IPHONE 1 #define JUCE_IOS 1 @@ -137,12 +140,12 @@ #define JUCE_INTEL 1 #endif - #if JUCE_MAC && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4 - #error "Building for OSX 10.3 is no longer supported!" + #if JUCE_MAC && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + #error "Building for OSX 10.4 is no longer supported!" #endif - #if JUCE_MAC && ! defined (MAC_OS_X_VERSION_10_5) - #error "To build with 10.4 compatibility, use a 10.5 or 10.6 SDK and set the deployment target to 10.4" + #if JUCE_MAC && ! defined (MAC_OS_X_VERSION_10_6) + #error "To build with 10.5 compatibility, use a later SDK and set the deployment target to 10.5" #endif #endif diff --git a/source/modules/juce_core/text/juce_CharPointer_ASCII.h b/source/modules/juce_core/text/juce_CharPointer_ASCII.h index e85af4ba6..a58028aae 100644 --- a/source/modules/juce_core/text/juce_CharPointer_ASCII.h +++ b/source/modules/juce_core/text/juce_CharPointer_ASCII.h @@ -45,7 +45,7 @@ public: typedef char CharType; inline explicit CharPointer_ASCII (const CharType* const rawPointer) noexcept - : data (const_cast (rawPointer)) + : data (const_cast (rawPointer)) { } @@ -62,7 +62,7 @@ public: inline CharPointer_ASCII operator= (const CharType* text) noexcept { - data = const_cast (text); + data = const_cast (text); return *this; } diff --git a/source/modules/juce_core/text/juce_CharPointer_UTF16.h b/source/modules/juce_core/text/juce_CharPointer_UTF16.h index 7a245e136..8ebbd2bc5 100644 --- a/source/modules/juce_core/text/juce_CharPointer_UTF16.h +++ b/source/modules/juce_core/text/juce_CharPointer_UTF16.h @@ -46,7 +46,7 @@ public: #endif inline explicit CharPointer_UTF16 (const CharType* const rawPointer) noexcept - : data (const_cast (rawPointer)) + : data (const_cast (rawPointer)) { } @@ -63,7 +63,7 @@ public: inline CharPointer_UTF16 operator= (const CharType* text) noexcept { - data = const_cast (text); + data = const_cast (text); return *this; } @@ -471,7 +471,7 @@ public: /** Atomically swaps this pointer for a new value, returning the previous value. */ CharPointer_UTF16 atomicSwap (const CharPointer_UTF16 newValue) { - return CharPointer_UTF16 (reinterpret_cast &> (data).exchange (newValue.data)); + return CharPointer_UTF16 (reinterpret_cast&> (data).exchange (newValue.data)); } /** These values are the byte-order-mark (BOM) values for a UTF-16 stream. */ diff --git a/source/modules/juce_core/text/juce_CharPointer_UTF32.h b/source/modules/juce_core/text/juce_CharPointer_UTF32.h index 9e96d7057..1ce92c32c 100644 --- a/source/modules/juce_core/text/juce_CharPointer_UTF32.h +++ b/source/modules/juce_core/text/juce_CharPointer_UTF32.h @@ -42,7 +42,7 @@ public: typedef juce_wchar CharType; inline explicit CharPointer_UTF32 (const CharType* const rawPointer) noexcept - : data (const_cast (rawPointer)) + : data (const_cast (rawPointer)) { } @@ -59,7 +59,7 @@ public: inline CharPointer_UTF32 operator= (const CharType* text) noexcept { - data = const_cast (text); + data = const_cast (text); return *this; } @@ -367,7 +367,7 @@ public: /** Atomically swaps this pointer for a new value, returning the previous value. */ CharPointer_UTF32 atomicSwap (const CharPointer_UTF32 newValue) { - return CharPointer_UTF32 (reinterpret_cast &> (data).exchange (newValue.data)); + return CharPointer_UTF32 (reinterpret_cast&> (data).exchange (newValue.data)); } private: diff --git a/source/modules/juce_core/text/juce_CharPointer_UTF8.h b/source/modules/juce_core/text/juce_CharPointer_UTF8.h index 107d9c4a9..ba4ac186e 100644 --- a/source/modules/juce_core/text/juce_CharPointer_UTF8.h +++ b/source/modules/juce_core/text/juce_CharPointer_UTF8.h @@ -41,7 +41,7 @@ public: typedef char CharType; inline explicit CharPointer_UTF8 (const CharType* const rawPointer) noexcept - : data (const_cast (rawPointer)) + : data (const_cast (rawPointer)) { } @@ -58,7 +58,7 @@ public: inline CharPointer_UTF8 operator= (const CharType* text) noexcept { - data = const_cast (text); + data = const_cast (text); return *this; } @@ -542,7 +542,7 @@ public: /** Atomically swaps this pointer for a new value, returning the previous value. */ CharPointer_UTF8 atomicSwap (const CharPointer_UTF8 newValue) { - return CharPointer_UTF8 (reinterpret_cast &> (data).exchange (newValue.data)); + return CharPointer_UTF8 (reinterpret_cast&> (data).exchange (newValue.data)); } /** These values are the byte-order mark (BOM) values for a UTF-8 stream. */ diff --git a/source/modules/juce_core/text/juce_String.cpp b/source/modules/juce_core/text/juce_String.cpp index 7527ca0a5..02cd66e29 100644 --- a/source/modules/juce_core/text/juce_String.cpp +++ b/source/modules/juce_core/text/juce_String.cpp @@ -821,10 +821,10 @@ namespace StringHelpers char* end = buffer + numElementsInArray (buffer); char* start = NumberToStringConverters::numberToString (end, number); - #if (JUCE_STRING_UTF_TYPE == 8) + #if JUCE_STRING_UTF_TYPE == 8 str.appendCharPointer (String::CharPointerType (start), String::CharPointerType (end)); #else - str.appendCharPointer (String::CharPointer_ASCII (start), String::CharPointer_ASCII (end)); + str.appendCharPointer (CharPointer_ASCII (start), CharPointer_ASCII (end)); #endif return str; @@ -1948,7 +1948,7 @@ String String::toHexString (const void* const d, const int size, const int group String s (PreallocationBytes (sizeof (CharPointerType::CharType) * (size_t) numChars)); - const unsigned char* data = static_cast (d); + const unsigned char* data = static_cast (d); CharPointerType dest (s.text); for (int i = 0; i < size; ++i) diff --git a/source/modules/juce_core/text/juce_StringArray.cpp b/source/modules/juce_core/text/juce_StringArray.cpp index 7ccdcb863..dc331d8dd 100644 --- a/source/modules/juce_core/text/juce_StringArray.cpp +++ b/source/modules/juce_core/text/juce_StringArray.cpp @@ -37,7 +37,7 @@ StringArray::StringArray (const StringArray& other) #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS StringArray::StringArray (StringArray&& other) noexcept - : strings (static_cast &&> (other.strings)) + : strings (static_cast&&> (other.strings)) { } #endif @@ -81,7 +81,7 @@ StringArray& StringArray::operator= (const StringArray& other) #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS StringArray& StringArray::operator= (StringArray&& other) noexcept { - strings = static_cast &&> (other.strings); + strings = static_cast&&> (other.strings); return *this; } #endif diff --git a/source/modules/juce_core/text/juce_StringArray.h b/source/modules/juce_core/text/juce_StringArray.h index a36d64e2c..ee39e2993 100644 --- a/source/modules/juce_core/text/juce_StringArray.h +++ b/source/modules/juce_core/text/juce_StringArray.h @@ -302,8 +302,8 @@ public: void remove (int index); /** Finds a string in the array and removes it. - This will remove the first occurrence of the given string from the array. The - comparison may be case-insensitive depending on the ignoreCase parameter. + This will remove all occurrences of the given string from the array. + The comparison may be case-insensitive depending on the ignoreCase parameter. */ void removeString (StringRef stringToRemove, bool ignoreCase = false); diff --git a/source/modules/juce_core/threads/juce_Process.h b/source/modules/juce_core/threads/juce_Process.h index eb5d59df8..5274be3b8 100644 --- a/source/modules/juce_core/threads/juce_Process.h +++ b/source/modules/juce_core/threads/juce_Process.h @@ -98,7 +98,7 @@ public: //============================================================================== /** Returns true if this process is being hosted by a debugger. */ - static bool JUCE_CALLTYPE isRunningUnderDebugger(); + static bool JUCE_CALLTYPE isRunningUnderDebugger() noexcept; //============================================================================== diff --git a/source/modules/juce_core/threads/juce_Thread.cpp b/source/modules/juce_core/threads/juce_Thread.cpp index dce488dbc..721f7410b 100644 --- a/source/modules/juce_core/threads/juce_Thread.cpp +++ b/source/modules/juce_core/threads/juce_Thread.cpp @@ -57,7 +57,7 @@ struct CurrentThreadHolder : public ReferenceCountedObject { CurrentThreadHolder() noexcept {} - typedef ReferenceCountedObjectPtr Ptr; + typedef ReferenceCountedObjectPtr Ptr; ThreadLocalValue value; JUCE_DECLARE_NON_COPYABLE (CurrentThreadHolder) @@ -86,22 +86,25 @@ void Thread::threadEntryPoint() const CurrentThreadHolder::Ptr currentThreadHolder (getCurrentThreadHolder()); currentThreadHolder->value = this; - JUCE_TRY - { - if (threadName.isNotEmpty()) - setCurrentThreadName (threadName); + if (threadName.isNotEmpty()) + setCurrentThreadName (threadName); - if (startSuspensionEvent.wait (10000)) - { - jassert (getCurrentThreadId() == threadId); + if (startSuspensionEvent.wait (10000)) + { + jassert (getCurrentThreadId() == threadId); - if (affinityMask != 0) - setCurrentThreadAffinityMask (affinityMask); + if (affinityMask != 0) + setCurrentThreadAffinityMask (affinityMask); + try + { run(); } + catch (...) + { + jassertfalse; // Your run() method mustn't throw any exceptions! + } } - JUCE_CATCH_ALL_ASSERT currentThreadHolder->value.releaseCurrentThreadStorage(); closeThreadHandle(); @@ -110,7 +113,7 @@ void Thread::threadEntryPoint() // used to wrap the incoming call from the platform-specific code void JUCE_API juce_threadEntryPoint (void* userData) { - static_cast (userData)->threadEntryPoint(); + static_cast (userData)->threadEntryPoint(); } //============================================================================== @@ -265,6 +268,12 @@ void SpinLock::enter() const noexcept } } +//============================================================================== +bool JUCE_CALLTYPE Process::isRunningUnderDebugger() noexcept +{ + return juce_isRunningUnderDebugger(); +} + //============================================================================== #if JUCE_UNIT_TESTS diff --git a/source/modules/juce_core/threads/juce_ThreadPool.cpp b/source/modules/juce_core/threads/juce_ThreadPool.cpp index 405161019..f57359c62 100644 --- a/source/modules/juce_core/threads/juce_ThreadPool.cpp +++ b/source/modules/juce_core/threads/juce_ThreadPool.cpp @@ -157,13 +157,13 @@ ThreadPoolJob* ThreadPool::getJob (const int index) const bool ThreadPool::contains (const ThreadPoolJob* const job) const { const ScopedLock sl (lock); - return jobs.contains (const_cast (job)); + return jobs.contains (const_cast (job)); } bool ThreadPool::isJobRunning (const ThreadPoolJob* const job) const { const ScopedLock sl (lock); - return jobs.contains (const_cast (job)) && job->isActive; + return jobs.contains (const_cast (job)) && job->isActive; } bool ThreadPool::waitForJobToFinish (const ThreadPoolJob* const job, const int timeOutMs) const @@ -336,11 +336,14 @@ bool ThreadPool::runNextJob (ThreadPoolThread& thread) ThreadPoolJob::JobStatus result = ThreadPoolJob::jobHasFinished; thread.currentJob = job; - JUCE_TRY + try { result = job->runJob(); } - JUCE_CATCH_ALL_ASSERT + catch (...) + { + jassertfalse; // Your runJob() method mustn't throw any exceptions! + } thread.currentJob = nullptr; diff --git a/source/modules/juce_core/unit_tests/juce_UnitTest.cpp b/source/modules/juce_core/unit_tests/juce_UnitTest.cpp index 597314d68..1f64182c4 100644 --- a/source/modules/juce_core/unit_tests/juce_UnitTest.cpp +++ b/source/modules/juce_core/unit_tests/juce_UnitTest.cpp @@ -205,9 +205,9 @@ void UnitTestRunner::endTest() m << r->failures << (r->failures == 1 ? " test" : " tests") << " failed, out of a total of " << (r->passes + r->failures); - logMessage (String::empty); + logMessage (String()); logMessage (m); - logMessage (String::empty); + logMessage (String()); } else { diff --git a/source/modules/juce_core/unit_tests/juce_UnitTest.h b/source/modules/juce_core/unit_tests/juce_UnitTest.h index 9ea9689ce..d698c95aa 100644 --- a/source/modules/juce_core/unit_tests/juce_UnitTest.h +++ b/source/modules/juce_core/unit_tests/juce_UnitTest.h @@ -136,13 +136,13 @@ public: If testResult is true, a pass is logged; if it's false, a failure is logged. If the failure message is specified, it will be written to the log if the test fails. */ - void expect (bool testResult, const String& failureMessage = String::empty); + void expect (bool testResult, const String& failureMessage = String()); /** Compares two values, and if they don't match, prints out a message containing the expected and actual result values. */ template - void expectEquals (ValueType actual, ValueType expected, String failureMessage = String::empty) + void expectEquals (ValueType actual, ValueType expected, String failureMessage = String()) { const bool result = (actual == expected); diff --git a/source/modules/juce_core/xml/juce_XmlDocument.cpp b/source/modules/juce_core/xml/juce_XmlDocument.cpp index de77013b3..c0f016e3b 100644 --- a/source/modules/juce_core/xml/juce_XmlDocument.cpp +++ b/source/modules/juce_core/xml/juce_XmlDocument.cpp @@ -172,7 +172,7 @@ String XmlDocument::getFileContents (const String& filename) const return in->readEntireStreamAsString(); } - return String::empty; + return String(); } juce_wchar XmlDocument::readNextChar() noexcept @@ -759,10 +759,10 @@ String XmlDocument::expandEntity (const String& ent) const juce_wchar char1 = ent[1]; if (char1 == 'x' || char1 == 'X') - return String::charToString (static_cast (ent.substring (2).getHexValue32())); + return String::charToString (static_cast (ent.substring (2).getHexValue32())); if (char1 >= '0' && char1 <= '9') - return String::charToString (static_cast (ent.substring (1).getIntValue())); + return String::charToString (static_cast (ent.substring (1).getIntValue())); setLastError ("illegal escape sequence", false); return String::charToString ('&'); diff --git a/source/modules/juce_core/xml/juce_XmlElement.h b/source/modules/juce_core/xml/juce_XmlElement.h index c253dbe15..2d23c6b75 100644 --- a/source/modules/juce_core/xml/juce_XmlElement.h +++ b/source/modules/juce_core/xml/juce_XmlElement.h @@ -134,7 +134,7 @@ } // now we can turn the whole thing into a text document.. - String myXmlDoc = animalsList.createDocument (String::empty); + String myXmlDoc = animalsList.createDocument (String()); @endcode @see XmlDocument @@ -642,7 +642,7 @@ public: if (num > 1) { - HeapBlock elems ((size_t) num); + HeapBlock elems ((size_t) num); getChildElementsAsArray (elems); sortArray (comparator, (XmlElement**) elems, 0, num - 1, retainOrderOfEquivalentItems); reorderChildElements (elems, num); diff --git a/source/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp b/source/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp index ec90603ac..7ee8a9175 100644 --- a/source/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp +++ b/source/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp @@ -85,7 +85,7 @@ private: if (streamIsValid) { - stream.next_in = const_cast (data); + stream.next_in = const_cast (data); stream.next_out = buffer; stream.avail_in = (z_uInt) dataSize; stream.avail_out = (z_uInt) sizeof (buffer); @@ -144,7 +144,7 @@ bool GZIPCompressorOutputStream::write (const void* destBuffer, size_t howMany) { jassert (destBuffer != nullptr && (ssize_t) howMany >= 0); - return helper->write (static_cast (destBuffer), howMany, *destStream); + return helper->write (static_cast (destBuffer), howMany, *destStream); } int64 GZIPCompressorOutputStream::getPosition() diff --git a/source/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp b/source/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp index 88d49a334..422f17c6e 100644 --- a/source/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp +++ b/source/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp @@ -226,7 +226,7 @@ int GZIPDecompressorInputStream::read (void* destBuffer, int howMany) if (howMany > 0 && ! isEof) { int numRead = 0; - uint8* d = static_cast (destBuffer); + uint8* d = static_cast (destBuffer); while (! helper->error) { diff --git a/source/modules/juce_core/zip/juce_GZIPDecompressorInputStream.h b/source/modules/juce_core/zip/juce_GZIPDecompressorInputStream.h index cdd9fd270..8c2b9bf0a 100644 --- a/source/modules/juce_core/zip/juce_GZIPDecompressorInputStream.h +++ b/source/modules/juce_core/zip/juce_GZIPDecompressorInputStream.h @@ -92,7 +92,7 @@ private: bool isEof; int activeBufferSize; int64 originalSourcePos, currentPos; - HeapBlock buffer; + HeapBlock buffer; class GZIPDecompressHelper; friend struct ContainerDeletePolicy; diff --git a/source/modules/juce_core/zip/juce_ZipFile.h b/source/modules/juce_core/zip/juce_ZipFile.h index 43f3c1017..4d45da2cb 100644 --- a/source/modules/juce_core/zip/juce_ZipFile.h +++ b/source/modules/juce_core/zip/juce_ZipFile.h @@ -181,18 +181,18 @@ public: Create a ZipFile::Builder object, and call its addFile() method to add some files, then you can write it to a stream with write(). - - Currently this just stores the files with no compression.. That will be added - soon! */ - class Builder + class JUCE_API Builder { public: + /** Creates an empty builder object. */ Builder(); + + /** Destructor. */ ~Builder(); - /** Adds a file while should be added to the archive. - The file isn't read immediately, all the files will be read later when the writeToStream() + /** Adds a file to the list of items which will be added to the archive. + The file isn't read immediately: the files will be read later when the writeToStream() method is called. The compressionLevel can be between 0 (no compression), and 9 (maximum compression). @@ -202,7 +202,7 @@ public: void addFile (const File& fileToAdd, int compressionLevel, const String& storedPathName = String()); - /** Adds a file while should be added to the archive. + /** Adds a stream to the list of items which will be added to the archive. @param streamToRead this stream isn't read immediately - a pointer to the stream is stored, then used later when the writeToStream() method is called, and diff --git a/source/modules/juce_data_structures.h b/source/modules/juce_data_structures.h deleted file mode 100644 index 16c3436f6..000000000 --- a/source/modules/juce_data_structures.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Carla Juce setup - * Copyright (C) 2013-2014 Filipe Coelho - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or any later version. - * - * This program 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. - * - * For a full copy of the GNU General Public License see the doc/GPL.txt file. - */ - -#ifndef CARLA_JUCE_DATA_STRUCTURES_H_INCLUDED -#define CARLA_JUCE_DATA_STRUCTURES_H_INCLUDED - -#include "juce_events.h" - -#if JUCE_MAC || JUCE_WINDOWS -# include "juce_data_structures/AppConfig.h" -# include "juce_data_structures/juce_data_structures.h" -#endif - -#endif // CARLA_JUCE_DATA_STRUCTURES_H_INCLUDED diff --git a/source/modules/juce_data_structures/AppConfig.h b/source/modules/juce_data_structures/AppConfig.h deleted file mode 100755 index 92bea407b..000000000 --- a/source/modules/juce_data_structures/AppConfig.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - ============================================================================== - - Build options for juce_data_structures static library - - ============================================================================== -*/ - -#ifndef CARLA_JUCE_DATA_STRUCTURES_APPCONFIG_H_INCLUDED -#define CARLA_JUCE_DATA_STRUCTURES_APPCONFIG_H_INCLUDED - -#include "../juce_events/AppConfig.h" - -#endif // CARLA_JUCE_DATA_STRUCTURES_APPCONFIG_H_INCLUDED diff --git a/source/modules/juce_data_structures/Makefile b/source/modules/juce_data_structures/Makefile index 915411d78..999b49d1b 100644 --- a/source/modules/juce_data_structures/Makefile +++ b/source/modules/juce_data_structures/Makefile @@ -10,7 +10,7 @@ include ../Makefile.mk # ---------------------------------------------------------------------------------------------------------------------------- -BUILD_CXX_FLAGS += $(JUCE_DATA_STRUCTURES_FLAGS) -w +BUILD_CXX_FLAGS += $(JUCE_DATA_STRUCTURES_FLAGS) -I.. # ---------------------------------------------------------------------------------------------------------------------------- diff --git a/source/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h b/source/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h index a047de839..fa201313d 100644 --- a/source/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h +++ b/source/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h @@ -120,7 +120,7 @@ public: private: //============================================================================== PropertiesFile::Options options; - ScopedPointer userProps, commonProps; + ScopedPointer userProps, commonProps; int commonSettingsAreReadOnly; void openFiles(); diff --git a/source/modules/juce_data_structures/juce_data_structures.cpp b/source/modules/juce_data_structures/juce_data_structures.cpp index 0d2b8759c..e6d7784a4 100644 --- a/source/modules/juce_data_structures/juce_data_structures.cpp +++ b/source/modules/juce_data_structures/juce_data_structures.cpp @@ -22,7 +22,7 @@ ============================================================================== */ -#if defined (JUCE_DATA_STRUCTURES_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE +#ifdef JUCE_DATA_STRUCTURES_H_INCLUDED /* 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 @@ -31,10 +31,6 @@ #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_data_structures.h" namespace juce diff --git a/source/modules/juce_data_structures/undomanager/juce_UndoableAction.h b/source/modules/juce_data_structures/undomanager/juce_UndoableAction.h index ff22895be..479dd7692 100644 --- a/source/modules/juce_data_structures/undomanager/juce_UndoableAction.h +++ b/source/modules/juce_data_structures/undomanager/juce_UndoableAction.h @@ -92,7 +92,7 @@ public: If it's not possible to merge the two actions, the method should return zero. */ - virtual UndoableAction* createCoalescedAction (UndoableAction* nextAction) { (void) nextAction; return nullptr; } + virtual UndoableAction* createCoalescedAction (UndoableAction* nextAction) { ignoreUnused (nextAction); return nullptr; } }; diff --git a/source/modules/juce_events.h b/source/modules/juce_events.h deleted file mode 100644 index d71fba9f6..000000000 --- a/source/modules/juce_events.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Carla Juce setup - * Copyright (C) 2013-2014 Filipe Coelho - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or any later version. - * - * This program 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. - * - * For a full copy of the GNU General Public License see the doc/GPL.txt file. - */ - -#ifndef CARLA_JUCE_EVENTS_H_INCLUDED -#define CARLA_JUCE_EVENTS_H_INCLUDED - -#include "juce_core.h" - -#if JUCE_MAC || JUCE_WINDOWS -# include "juce_events/AppConfig.h" -# include "juce_events/juce_events.h" -#endif - -#endif // CARLA_JUCE_EVENTS_H_INCLUDED diff --git a/source/modules/juce_events/AppConfig.h b/source/modules/juce_events/AppConfig.h deleted file mode 100755 index 0a65728c3..000000000 --- a/source/modules/juce_events/AppConfig.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - ============================================================================== - - Build options for juce_events static library - - ============================================================================== -*/ - -#ifndef CARLA_JUCE_EVENTS_APPCONFIG_H_INCLUDED -#define CARLA_JUCE_EVENTS_APPCONFIG_H_INCLUDED - -#include "../juce_core/AppConfig.h" - -#endif // CARLA_JUCE_EVENTS_APPCONFIG_H_INCLUDED diff --git a/source/modules/juce_events/Makefile b/source/modules/juce_events/Makefile index 95eab6484..8a77d309d 100644 --- a/source/modules/juce_events/Makefile +++ b/source/modules/juce_events/Makefile @@ -10,7 +10,11 @@ include ../Makefile.mk # ---------------------------------------------------------------------------------------------------------------------------- -BUILD_CXX_FLAGS += $(JUCE_EVENTS_FLAGS) -w +BUILD_CXX_FLAGS += $(JUCE_EVENTS_FLAGS) -I.. + +ifeq ($(WIN32),true) +BUILD_CXX_FLAGS += -Wno-missing-field-initializers +endif # ---------------------------------------------------------------------------------------------------------------------------- diff --git a/source/modules/juce_events/interprocess/juce_InterprocessConnection.h b/source/modules/juce_events/interprocess/juce_InterprocessConnection.h index 280ad54a9..23e9fe3d6 100644 --- a/source/modules/juce_events/interprocess/juce_InterprocessConnection.h +++ b/source/modules/juce_events/interprocess/juce_InterprocessConnection.h @@ -181,8 +181,8 @@ private: WeakReference::Master masterReference; friend class WeakReference; CriticalSection pipeAndSocketLock; - ScopedPointer socket; - ScopedPointer pipe; + ScopedPointer socket; + ScopedPointer pipe; bool callbackConnectionState; const bool useMessageThread; const uint32 magicMessageHeader; diff --git a/source/modules/juce_events/interprocess/juce_InterprocessConnectionServer.h b/source/modules/juce_events/interprocess/juce_InterprocessConnectionServer.h index 2bf0a2221..32350837b 100644 --- a/source/modules/juce_events/interprocess/juce_InterprocessConnectionServer.h +++ b/source/modules/juce_events/interprocess/juce_InterprocessConnectionServer.h @@ -82,7 +82,7 @@ protected: private: //============================================================================== - ScopedPointer socket; + ScopedPointer socket; void run() override; diff --git a/source/modules/juce_events/juce_events.cpp b/source/modules/juce_events/juce_events.cpp index 53069c472..89adf04dc 100644 --- a/source/modules/juce_events/juce_events.cpp +++ b/source/modules/juce_events/juce_events.cpp @@ -22,7 +22,7 @@ ============================================================================== */ -#if defined (JUCE_EVENTS_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE +#ifdef JUCE_EVENTS_H_INCLUDED /* 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 @@ -31,17 +31,9 @@ #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_events.h" -#if JUCE_CATCH_UNHANDLED_EXCEPTIONS && JUCE_MODULE_AVAILABLE_juce_gui_basics - #include "../juce_gui_basics/juce_gui_basics.h" -#endif - //============================================================================== #if JUCE_MAC #import diff --git a/source/modules/juce_events/juce_events.h b/source/modules/juce_events/juce_events.h index 0ef1070b4..357df2de1 100644 --- a/source/modules/juce_events/juce_events.h +++ b/source/modules/juce_events/juce_events.h @@ -40,7 +40,6 @@ namespace juce #include "messages/juce_ApplicationBase.h" #include "messages/juce_Initialisation.h" #include "messages/juce_MountedVolumeListChangeDetector.h" -#include "broadcasters/juce_ListenerList.h" #include "broadcasters/juce_ActionBroadcaster.h" #include "broadcasters/juce_ActionListener.h" #include "broadcasters/juce_AsyncUpdater.h" diff --git a/source/modules/juce_events/messages/juce_ApplicationBase.h b/source/modules/juce_events/messages/juce_ApplicationBase.h index bde123bf8..35bc65ff7 100644 --- a/source/modules/juce_events/messages/juce_ApplicationBase.h +++ b/source/modules/juce_events/messages/juce_ApplicationBase.h @@ -279,4 +279,27 @@ private: }; +//============================================================================== +#if JUCE_CATCH_UNHANDLED_EXCEPTIONS || defined (DOXYGEN) + + /** The JUCE_TRY/JUCE_CATCH_EXCEPTION wrappers can be used to pass any uncaught exceptions to + the JUCEApplicationBase::sendUnhandledException() method. + This functionality can be enabled with the JUCE_CATCH_UNHANDLED_EXCEPTIONS macro. + */ + #define JUCE_TRY try + + /** The JUCE_TRY/JUCE_CATCH_EXCEPTION wrappers can be used to pass any uncaught exceptions to + the JUCEApplicationBase::sendUnhandledException() method. + This functionality can be enabled with the JUCE_CATCH_UNHANDLED_EXCEPTIONS macro. + */ + #define JUCE_CATCH_EXCEPTION \ + catch (const std::exception& e) { juce::JUCEApplicationBase::sendUnhandledException (&e, __FILE__, __LINE__); } \ + catch (...) { juce::JUCEApplicationBase::sendUnhandledException (nullptr, __FILE__, __LINE__); } + +#else + #define JUCE_TRY + #define JUCE_CATCH_EXCEPTION +#endif + + #endif // JUCE_APPLICATIONBASE_H_INCLUDED diff --git a/source/modules/juce_events/messages/juce_MessageListener.cpp b/source/modules/juce_events/messages/juce_MessageListener.cpp index 6bcd25e51..ed75dd499 100644 --- a/source/modules/juce_events/messages/juce_MessageListener.cpp +++ b/source/modules/juce_events/messages/juce_MessageListener.cpp @@ -44,6 +44,6 @@ MessageListener::~MessageListener() void MessageListener::postMessage (Message* const message) const { - message->recipient = const_cast (this); + message->recipient = const_cast (this); message->post(); } diff --git a/source/modules/juce_events/messages/juce_MessageManager.h b/source/modules/juce_events/messages/juce_MessageManager.h index 23b8a6234..676ced409 100644 --- a/source/modules/juce_events/messages/juce_MessageManager.h +++ b/source/modules/juce_events/messages/juce_MessageManager.h @@ -32,8 +32,7 @@ class ActionBroadcaster; //============================================================================== -/** See MessageManager::callFunctionOnMessageThread() for use of this function type -*/ +/** See MessageManager::callFunctionOnMessageThread() for use of this function type. */ typedef void* (MessageCallbackFunction) (void* userData); @@ -202,7 +201,7 @@ private: friend class QuitMessage; friend class MessageManagerLock; - ScopedPointer broadcaster; + ScopedPointer broadcaster; bool quitMessagePosted, quitMessageReceived; Thread::ThreadID messageThreadId; Thread::ThreadID volatile threadWithLock; @@ -243,6 +242,9 @@ private: Obviously be careful not to create one of these and leave it lying around, or your app will grind to a halt! + MessageManagerLocks are re-entrant, so can be safely nested if the current thread + already has the lock. + Another caveat is that using this in conjunction with other CriticalSections can create lots of interesting ways of producing a deadlock! In particular, if your message thread calls stopThread() for a thread that uses these locks, diff --git a/source/modules/juce_events/native/juce_linux_Messaging.cpp b/source/modules/juce_events/native/juce_linux_Messaging.cpp index a3e761138..f90199a12 100644 --- a/source/modules/juce_events/native/juce_linux_Messaging.cpp +++ b/source/modules/juce_events/native/juce_linux_Messaging.cpp @@ -49,7 +49,7 @@ public: totalEventCount (0) { int ret = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, fd); - (void) ret; jassert (ret == 0); + ignoreUnused (ret); jassert (ret == 0); } ~InternalMessageQueue() @@ -75,7 +75,7 @@ public: ScopedUnlock ul (lock); const unsigned char x = 0xff; ssize_t bytesWritten = write (fd[0], &x, 1); - (void) bytesWritten; + ignoreUnused (bytesWritten); } } @@ -187,7 +187,7 @@ private: const ScopedUnlock ul (lock); unsigned char x; ssize_t numBytes = read (fd[1], &x, 1); - (void) numBytes; + ignoreUnused (numBytes); } return queue.removeAndReturn (0); @@ -235,7 +235,7 @@ namespace LinuxErrorHandling int errorHandler (Display* display, XErrorEvent* event) { - (void) display; (void) event; + ignoreUnused (display, event); #if JUCE_DEBUG_XERRORS char errorStr[64] = { 0 }; diff --git a/source/modules/juce_events/native/juce_mac_MessageManager.mm b/source/modules/juce_events/native/juce_mac_MessageManager.mm index e1f6d87ab..b63282759 100644 --- a/source/modules/juce_events/native/juce_mac_MessageManager.mm +++ b/source/modules/juce_events/native/juce_mac_MessageManager.mm @@ -239,7 +239,7 @@ void MessageManager::runDispatchLoop() { // An AppKit exception will kill the app, but at least this provides a chance to log it., std::runtime_error ex (std::string ("NSException: ") + [[e name] UTF8String] + ", Reason:" + [[e reason] UTF8String]); - JUCEApplication::sendUnhandledException (&ex, __FILE__, __LINE__); + JUCEApplicationBase::sendUnhandledException (&ex, __FILE__, __LINE__); } @finally { @@ -330,14 +330,6 @@ void MessageManager::doPlatformSpecificInitialisation() { if (appDelegate == nil) appDelegate = new AppDelegate(); - - #if ! (defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) - // This launches a dummy thread, which forces Cocoa to initialise NSThreads correctly (needed prior to 10.5) - if (! [NSThread isMultiThreaded]) - [NSThread detachNewThreadSelector: @selector (dummyMethod) - toTarget: appDelegate->delegate - withObject: nil]; - #endif } void MessageManager::doPlatformSpecificShutdown() diff --git a/source/modules/juce_events/native/juce_osx_MessageQueue.h b/source/modules/juce_events/native/juce_osx_MessageQueue.h index 4b3656407..7042f03a1 100644 --- a/source/modules/juce_events/native/juce_osx_MessageQueue.h +++ b/source/modules/juce_events/native/juce_osx_MessageQueue.h @@ -32,10 +32,10 @@ class MessageQueue public: MessageQueue() { - #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 && ! JUCE_IOS - runLoop = CFRunLoopGetMain(); - #else + #if JUCE_IOS runLoop = CFRunLoopGetCurrent(); + #else + runLoop = CFRunLoopGetMain(); #endif CFRunLoopSourceContext sourceContext; @@ -96,7 +96,7 @@ private: static void runLoopSourceCallback (void* info) { - static_cast (info)->runLoopCallback(); + static_cast (info)->runLoopCallback(); } }; diff --git a/source/modules/juce_events/native/juce_win32_Messaging.cpp b/source/modules/juce_events/native/juce_win32_Messaging.cpp index 7c027529b..36621acb6 100644 --- a/source/modules/juce_events/native/juce_win32_Messaging.cpp +++ b/source/modules/juce_events/native/juce_win32_Messaging.cpp @@ -38,15 +38,16 @@ namespace WindowsMessageHelpers void dispatchMessageFromLParam (LPARAM lParam) { - MessageManager::MessageBase* const message = reinterpret_cast (lParam); - - JUCE_TRY + if (MessageManager::MessageBase* const message = reinterpret_cast (lParam)) { - message->messageCallback(); - } - JUCE_CATCH_EXCEPTION + JUCE_TRY + { + message->messageCallback(); + } + JUCE_CATCH_EXCEPTION - message->decReferenceCount(); + message->decReferenceCount(); + } } //============================================================================== @@ -64,22 +65,27 @@ namespace WindowsMessageHelpers if (message == broadcastId) { - const ScopedPointer messageString ((String*) lParam); - MessageManager::getInstance()->deliverBroadcastMessage (*messageString); + if (String* const m = reinterpret_cast (lParam)) + { + const ScopedPointer messageString (m); + MessageManager::getInstance()->deliverBroadcastMessage (*m); + } + return 0; } if (message == WM_COPYDATA) { - const COPYDATASTRUCT* const data = reinterpret_cast (lParam); - - if (data->dwData == broadcastId) + if (const COPYDATASTRUCT* const data = reinterpret_cast (lParam)) { - const String messageString (CharPointer_UTF32 ((const CharPointer_UTF32::CharType*) data->lpData), - data->cbData / sizeof (CharPointer_UTF32::CharType)); - - PostMessage (juce_messageWindowHandle, broadcastId, 0, (LPARAM) new String (messageString)); - return 0; + if (data->dwData == broadcastId) + { + const String messageString (CharPointer_UTF32 ((const CharPointer_UTF32::CharType*) data->lpData), + data->cbData / sizeof (CharPointer_UTF32::CharType)); + + PostMessage (juce_messageWindowHandle, broadcastId, 0, (LPARAM) new String (messageString)); + return 0; + } } } } diff --git a/source/modules/juce_graphics.h b/source/modules/juce_graphics.h deleted file mode 100644 index 8d94cab27..000000000 --- a/source/modules/juce_graphics.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Carla Juce setup - * Copyright (C) 2013-2014 Filipe Coelho - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or any later version. - * - * This program 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. - * - * For a full copy of the GNU General Public License see the doc/GPL.txt file. - */ - -#ifndef CARLA_JUCE_GRAPHICS_H_INCLUDED -#define CARLA_JUCE_GRAPHICS_H_INCLUDED - -#include "juce_core.h" -#include "juce_events.h" - -#if JUCE_MAC || JUCE_WINDOWS -# include "juce_graphics/AppConfig.h" -# include "juce_graphics/juce_graphics.h" -#endif - -#endif // CARLA_JUCE_GRAPHICS_H_INCLUDED diff --git a/source/modules/juce_graphics/AppConfig.h b/source/modules/juce_graphics/AppConfig.h deleted file mode 100755 index dc638e376..000000000 --- a/source/modules/juce_graphics/AppConfig.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - ============================================================================== - - Build options for juce_graphics static library - - ============================================================================== -*/ - -#ifndef CARLA_JUCE_GRAPHICS_APPCONFIG_H_INCLUDED -#define CARLA_JUCE_GRAPHICS_APPCONFIG_H_INCLUDED - -#include "../juce_core/AppConfig.h" -#include "../juce_events/AppConfig.h" - -//============================================================================= -/** Config: JUCE_USE_COREIMAGE_LOADER - - On OSX, enabling this flag means that the CoreImage codecs will be used to load - PNG/JPEG/GIF files. It is enabled by default, but you may want to disable it if - you'd rather use libpng, libjpeg, etc. -*/ -#define JUCE_USE_COREIMAGE_LOADER 1 - -/** Config: JUCE_USE_DIRECTWRITE - - Enabling this flag means that DirectWrite will be used when available for font - management and layout. -*/ -#define JUCE_USE_DIRECTWRITE 1 - -#define JUCE_INCLUDE_PNGLIB_CODE 1 - -#define JUCE_INCLUDE_JPEGLIB_CODE 1 - -#define USE_COREGRAPHICS_RENDERING 1 - -#endif // CARLA_JUCE_GRAPHICS_APPCONFIG_H_INCLUDED diff --git a/source/modules/juce_graphics/Makefile b/source/modules/juce_graphics/Makefile index d3a63f1d7..64f273573 100644 --- a/source/modules/juce_graphics/Makefile +++ b/source/modules/juce_graphics/Makefile @@ -10,7 +10,11 @@ include ../Makefile.mk # ---------------------------------------------------------------------------------------------------------------------------- -BUILD_CXX_FLAGS += $(JUCE_GRAPHICS_FLAGS) -w +BUILD_CXX_FLAGS += $(JUCE_GRAPHICS_FLAGS) -I.. + +ifeq ($(WIN32),true) +BUILD_CXX_FLAGS += -Wno-missing-field-initializers -Wno-strict-overflow +endif # ---------------------------------------------------------------------------------------------------------------------------- diff --git a/source/modules/juce_graphics/colour/juce_ColourGradient.cpp b/source/modules/juce_graphics/colour/juce_ColourGradient.cpp index 87ccb5692..59b87f3f3 100644 --- a/source/modules/juce_graphics/colour/juce_ColourGradient.cpp +++ b/source/modules/juce_graphics/colour/juce_ColourGradient.cpp @@ -178,7 +178,7 @@ void ColourGradient::createLookupTable (PixelARGB* const lookupTable, const int lookupTable [index++] = pix1; } -int ColourGradient::createLookupTable (const AffineTransform& transform, HeapBlock & lookupTable) const +int ColourGradient::createLookupTable (const AffineTransform& transform, HeapBlock& lookupTable) const { JUCE_COLOURGRADIENT_CHECK_COORDS_INITIALISED // Trying to use this object without setting its coordinates? jassert (colours.size() >= 2); diff --git a/source/modules/juce_graphics/colour/juce_ColourGradient.h b/source/modules/juce_graphics/colour/juce_ColourGradient.h index 9349d82b3..542985b9c 100644 --- a/source/modules/juce_graphics/colour/juce_ColourGradient.h +++ b/source/modules/juce_graphics/colour/juce_ColourGradient.h @@ -126,7 +126,7 @@ public: colours that it added. When calling this, the ColourGradient must have at least 2 colour stops specified. */ - int createLookupTable (const AffineTransform& transform, HeapBlock & resultLookupTable) const; + int createLookupTable (const AffineTransform& transform, HeapBlock& resultLookupTable) const; /** Creates a set of interpolated premultiplied ARGB values. This will fill an array of a user-specified size with the gradient, interpolating to fit. diff --git a/source/modules/juce_graphics/colour/juce_FillType.cpp b/source/modules/juce_graphics/colour/juce_FillType.cpp index c477f71d9..34d6da9c6 100644 --- a/source/modules/juce_graphics/colour/juce_FillType.cpp +++ b/source/modules/juce_graphics/colour/juce_FillType.cpp @@ -67,7 +67,7 @@ FillType& FillType::operator= (const FillType& other) FillType::FillType (FillType&& other) noexcept : colour (other.colour), gradient (other.gradient.release()), - image (static_cast (other.image)), + image (static_cast (other.image)), transform (other.transform) { } @@ -78,7 +78,7 @@ FillType& FillType::operator= (FillType&& other) noexcept colour = other.colour; gradient = other.gradient.release(); - image = static_cast (other.image); + image = static_cast (other.image); transform = other.transform; return *this; } diff --git a/source/modules/juce_graphics/colour/juce_FillType.h b/source/modules/juce_graphics/colour/juce_FillType.h index 3cf037d14..28effc0f4 100644 --- a/source/modules/juce_graphics/colour/juce_FillType.h +++ b/source/modules/juce_graphics/colour/juce_FillType.h @@ -126,7 +126,7 @@ public: If a gradient is active, the overall opacity with which it should be applied is indicated by the alpha channel of the colour variable. */ - ScopedPointer gradient; + ScopedPointer gradient; /** The image that should be used for tiling. If an image fill is active, the overall opacity with which it should be applied diff --git a/source/modules/juce_graphics/contexts/juce_GraphicsContext.cpp b/source/modules/juce_graphics/contexts/juce_GraphicsContext.cpp index c6747f786..2efb3aa99 100644 --- a/source/modules/juce_graphics/contexts/juce_GraphicsContext.cpp +++ b/source/modules/juce_graphics/contexts/juce_GraphicsContext.cpp @@ -394,9 +394,15 @@ void Graphics::fillAll (Colour colourToUse) const //============================================================================== +void Graphics::fillPath (const Path& path) const +{ + if (! (context.isClipEmpty() || path.isEmpty())) + context.fillPath (path, AffineTransform()); +} + void Graphics::fillPath (const Path& path, const AffineTransform& transform) const { - if ((! context.isClipEmpty()) && ! path.isEmpty()) + if (! (context.isClipEmpty() || path.isEmpty())) context.fillPath (path, transform); } diff --git a/source/modules/juce_graphics/contexts/juce_GraphicsContext.h b/source/modules/juce_graphics/contexts/juce_GraphicsContext.h index bf5458135..3cfcc8cf6 100644 --- a/source/modules/juce_graphics/contexts/juce_GraphicsContext.h +++ b/source/modules/juce_graphics/contexts/juce_GraphicsContext.h @@ -436,13 +436,15 @@ public: //============================================================================== /** Fills a path using the currently selected colour or brush. */ - void fillPath (const Path& path, - const AffineTransform& transform = AffineTransform::identity) const; + void fillPath (const Path& path) const; + + /** Fills a path using the currently selected colour or brush, and adds a transform. */ + void fillPath (const Path& path, const AffineTransform& transform) const; /** Draws a path's outline using the currently selected colour or brush. */ void strokePath (const Path& path, const PathStrokeType& strokeType, - const AffineTransform& transform = AffineTransform::identity) const; + const AffineTransform& transform = AffineTransform()) const; /** Draws a line with an arrowhead at its end. @@ -607,7 +609,7 @@ public: @returns true if the resulting clipping region is non-zero in size @see reduceClipRegion */ - bool reduceClipRegion (const Path& path, const AffineTransform& transform = AffineTransform::identity); + bool reduceClipRegion (const Path& path, const AffineTransform& transform = AffineTransform()); /** Intersects the current clipping region with an image's alpha-channel. diff --git a/source/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp b/source/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp index d2d6a1ea4..3b7c1cf72 100644 --- a/source/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp +++ b/source/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp @@ -355,13 +355,13 @@ void LowLevelGraphicsPostScriptRenderer::fillRect (const Rectangle& r) { Path p; p.addRectangle (r); - fillPath (p, AffineTransform::identity); + fillPath (p, AffineTransform()); } } void LowLevelGraphicsPostScriptRenderer::fillRectList (const RectangleList& list) { - fillPath (list.toPath(), AffineTransform::identity); + fillPath (list.toPath(), AffineTransform()); } //============================================================================== @@ -510,7 +510,7 @@ void LowLevelGraphicsPostScriptRenderer::drawLine (const Line & line) { Path p; p.addLineSegment (line, 1.0f); - fillPath (p, AffineTransform::identity); + fillPath (p, AffineTransform()); } //============================================================================== diff --git a/source/modules/juce_graphics/fonts/juce_AttributedString.cpp b/source/modules/juce_graphics/fonts/juce_AttributedString.cpp index a5c5849ba..990e5d9a4 100644 --- a/source/modules/juce_graphics/fonts/juce_AttributedString.cpp +++ b/source/modules/juce_graphics/fonts/juce_AttributedString.cpp @@ -22,31 +22,156 @@ ============================================================================== */ -AttributedString::Attribute::Attribute (Range range_, Colour colour_) - : range (range_), colour (new Colour (colour_)) +namespace { + int getLength (const Array& atts) noexcept + { + return atts.size() != 0 ? atts.getReference (atts.size() - 1).range.getEnd() : 0; + } + + void splitAttributeRanges (Array& atts, int position) + { + for (int i = atts.size(); --i >= 0;) + { + const AttributedString::Attribute& att = atts.getReference (i); + const int offset = position - att.range.getStart(); + + if (offset >= 0) + { + if (offset > 0 && position < att.range.getEnd()) + { + atts.insert (i + 1, att); + atts.getReference (i).range.setEnd (position); + atts.getReference (i + 1).range.setStart (position); + } + + break; + } + } + } + + Range splitAttributeRanges (Array& atts, Range newRange) + { + newRange = newRange.getIntersectionWith (Range (0, getLength (atts))); + + if (! newRange.isEmpty()) + { + splitAttributeRanges (atts, newRange.getStart()); + splitAttributeRanges (atts, newRange.getEnd()); + } + + return newRange; + } + + void mergeAdjacentRanges (Array& atts) + { + for (int i = atts.size() - 1; --i >= 0;) + { + AttributedString::Attribute& a1 = atts.getReference (i); + AttributedString::Attribute& a2 = atts.getReference (i + 1); + + if (a1.colour == a2.colour && a1.font == a2.font) + { + a1.range.setEnd (a2.range.getEnd()); + atts.remove (i + 1); + + if (i < atts.size() - 1) + ++i; + } + } + } + + void appendRange (Array& atts, + int length, const Font* f, const Colour* c) + { + if (atts.size() == 0) + { + atts.add (AttributedString::Attribute (Range (0, length), + f != nullptr ? *f : Font(), + c != nullptr ? *c : Colour (0xff000000))); + } + else + { + const int start = getLength (atts); + atts.add (AttributedString::Attribute (Range (start, start + length), + f != nullptr ? *f : atts.getReference (atts.size() - 1).font, + c != nullptr ? *c : atts.getReference (atts.size() - 1).colour)); + mergeAdjacentRanges (atts); + } + } + + void applyFontAndColour (Array& atts, + Range range, const Font* f, const Colour* c) + { + range = splitAttributeRanges (atts, range); + + for (int i = 0; i < atts.size(); ++i) + { + AttributedString::Attribute& att = atts.getReference (i); + + if (range.getStart() < att.range.getEnd()) + { + if (range.getEnd() <= att.range.getStart()) + break; + + if (c != nullptr) att.colour = *c; + if (f != nullptr) att.font = *f; + } + } + + mergeAdjacentRanges (atts); + } + + void truncate (Array& atts, int newLength) + { + splitAttributeRanges (atts, newLength); + + for (int i = atts.size(); --i >= 0;) + if (atts.getReference (i).range.getStart() >= newLength) + atts.remove (i); + } } -AttributedString::Attribute::Attribute (Range range_, const Font& font_) - : range (range_), font (new Font (font_)) +//============================================================================== +AttributedString::Attribute::Attribute() noexcept : colour (0xff000000) {} +AttributedString::Attribute::~Attribute() noexcept {} + +#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS +AttributedString::Attribute::Attribute (Attribute&& other) noexcept + : range (other.range), + font (static_cast (other.font)), + colour (other.colour) +{ +} + +AttributedString::Attribute& AttributedString::Attribute::operator= (Attribute&& other) noexcept { + range = other.range; + font = static_cast (other.font); + colour = other.colour; + return *this; } +#endif -AttributedString::Attribute::Attribute (const Attribute& other) +AttributedString::Attribute::Attribute (const Attribute& other) noexcept : range (other.range), - font (other.font.createCopy()), - colour (other.colour.createCopy()) + font (other.font), + colour (other.colour) { } -AttributedString::Attribute::Attribute (const Attribute& other, const int offset) - : range (other.range + offset), - font (other.font.createCopy()), - colour (other.colour.createCopy()) +AttributedString::Attribute& AttributedString::Attribute::operator= (const Attribute& other) noexcept { + range = other.range; + font = other.font; + colour = other.colour; + return *this; } -AttributedString::Attribute::~Attribute() {} +AttributedString::Attribute::Attribute (Range r, const Font& f, Colour c) noexcept + : range (r), font (f), colour (c) +{ +} //============================================================================== AttributedString::AttributedString() @@ -58,12 +183,12 @@ AttributedString::AttributedString() } AttributedString::AttributedString (const String& newString) - : text (newString), - lineSpacing (0.0f), + : lineSpacing (0.0f), justification (Justification::left), wordWrap (AttributedString::byWord), readingDirection (AttributedString::natural) { + setText (newString); } AttributedString::AttributedString (const AttributedString& other) @@ -71,9 +196,9 @@ AttributedString::AttributedString (const AttributedString& other) lineSpacing (other.lineSpacing), justification (other.justification), wordWrap (other.wordWrap), - readingDirection (other.readingDirection) + readingDirection (other.readingDirection), + attributes (other.attributes) { - attributes.addCopiesOf (other.attributes); } AttributedString& AttributedString::operator= (const AttributedString& other) @@ -85,8 +210,7 @@ AttributedString& AttributedString::operator= (const AttributedString& other) justification = other.justification; wordWrap = other.wordWrap; readingDirection = other.readingDirection; - attributes.clear(); - attributes.addCopiesOf (other.attributes); + attributes = other.attributes; } return *this; @@ -94,74 +218,77 @@ AttributedString& AttributedString::operator= (const AttributedString& other) #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS AttributedString::AttributedString (AttributedString&& other) noexcept - : text (static_cast (other.text)), + : text (static_cast (other.text)), lineSpacing (other.lineSpacing), justification (other.justification), wordWrap (other.wordWrap), readingDirection (other.readingDirection), - attributes (static_cast &&> (other.attributes)) + attributes (static_cast&&> (other.attributes)) { } AttributedString& AttributedString::operator= (AttributedString&& other) noexcept { - text = static_cast (other.text); + text = static_cast (other.text); lineSpacing = other.lineSpacing; justification = other.justification; wordWrap = other.wordWrap; readingDirection = other.readingDirection; - attributes = static_cast &&> (other.attributes); + attributes = static_cast&&> (other.attributes); return *this; } #endif -AttributedString::~AttributedString() {} +AttributedString::~AttributedString() noexcept {} -void AttributedString::setText (const String& other) +void AttributedString::setText (const String& newText) { - text = other; + const int newLength = newText.length(); + const int oldLength = getLength (attributes); + + if (newLength > oldLength) + appendRange (attributes, newLength - oldLength, nullptr, nullptr); + else if (newLength < oldLength) + truncate (attributes, newLength); + + text = newText; } void AttributedString::append (const String& textToAppend) { text += textToAppend; + appendRange (attributes, textToAppend.length(), nullptr, nullptr); } void AttributedString::append (const String& textToAppend, const Font& font) { - const int oldLength = text.length(); - const int newLength = textToAppend.length(); - text += textToAppend; - setFont (Range (oldLength, oldLength + newLength), font); + appendRange (attributes, textToAppend.length(), &font, nullptr); } void AttributedString::append (const String& textToAppend, Colour colour) { - const int oldLength = text.length(); - const int newLength = textToAppend.length(); - text += textToAppend; - setColour (Range (oldLength, oldLength + newLength), colour); + appendRange (attributes, textToAppend.length(), nullptr, &colour); } void AttributedString::append (const String& textToAppend, const Font& font, Colour colour) { - const int oldLength = text.length(); - const int newLength = textToAppend.length(); - text += textToAppend; - setFont (Range (oldLength, oldLength + newLength), font); - setColour (Range (oldLength, oldLength + newLength), colour); + appendRange (attributes, textToAppend.length(), &font, &colour); } void AttributedString::append (const AttributedString& other) { - const int originalLength = text.length(); + const int originalLength = getLength (attributes); + const int originalNumAtts = attributes.size(); text += other.text; + attributes.addArray (other.attributes); - for (int i = 0; i < other.attributes.size(); ++i) - attributes.add (new Attribute (*other.attributes.getUnchecked(i), originalLength)); + for (int i = originalNumAtts; i < attributes.size(); ++i) + attributes.getReference (i).range += originalLength; + + mergeAdjacentRanges (attributes); } void AttributedString::clear() @@ -192,36 +319,30 @@ void AttributedString::setLineSpacing (const float newLineSpacing) noexcept void AttributedString::setColour (Range range, Colour colour) { - attributes.add (new Attribute (range, colour)); + applyFontAndColour (attributes, range, nullptr, &colour); } -void AttributedString::setColour (Colour colour) +void AttributedString::setFont (Range range, const Font& font) { - for (int i = attributes.size(); --i >= 0;) - if (attributes.getUnchecked(i)->getColour() != nullptr) - attributes.remove (i); - - setColour (Range (0, text.length()), colour); + applyFontAndColour (attributes, range, &font, nullptr); } -void AttributedString::setFont (Range range, const Font& font) +void AttributedString::setColour (Colour colour) { - attributes.add (new Attribute (range, font)); + setColour (Range (0, getLength (attributes)), colour); } void AttributedString::setFont (const Font& font) { - for (int i = attributes.size(); --i >= 0;) - if (attributes.getUnchecked(i)->getFont() != nullptr) - attributes.remove (i); - - setFont (Range (0, text.length()), font); + setFont (Range (0, getLength (attributes)), font); } void AttributedString::draw (Graphics& g, const Rectangle& area) const { if (text.isNotEmpty() && g.clipRegionIntersects (area.getSmallestIntegerContainer())) { + jassert (text.length() == getLength (attributes)); + if (! g.getInternalContext().drawTextLayout (*this, area)) { TextLayout layout; diff --git a/source/modules/juce_graphics/fonts/juce_AttributedString.h b/source/modules/juce_graphics/fonts/juce_AttributedString.h index 6f46e1f18..d32010304 100644 --- a/source/modules/juce_graphics/fonts/juce_AttributedString.h +++ b/source/modules/juce_graphics/fonts/juce_AttributedString.h @@ -53,7 +53,7 @@ public: #endif /** Destructor. */ - ~AttributedString(); + ~AttributedString() noexcept; //============================================================================== /** Returns the complete text of this attributed string. */ @@ -150,36 +150,28 @@ public: class JUCE_API Attribute { public: - /** Creates an attribute that changes the colour for a range of characters. - @see AttributedString::setColour() - */ - Attribute (Range range, Colour colour); - - /** Creates an attribute that changes the font for a range of characters. - @see AttributedString::setFont() - */ - Attribute (Range range, const Font& font); - - Attribute (const Attribute&); - ~Attribute(); - - /** If this attribute specifies a font, this returns it; otherwise it returns nullptr. */ - const Font* getFont() const noexcept { return font; } - - /** If this attribute specifies a colour, this returns it; otherwise it returns nullptr. */ - const Colour* getColour() const noexcept { return colour; } + Attribute() noexcept; + ~Attribute() noexcept; + Attribute (const Attribute&) noexcept; + Attribute& operator= (const Attribute&) noexcept; + #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS + Attribute (Attribute&&) noexcept; + Attribute& operator= (Attribute&&) noexcept; + #endif + + /** Creates an attribute that specifies the font and colour for a range of characters. */ + Attribute (Range range, const Font& font, Colour colour) noexcept; /** The range of characters to which this attribute will be applied. */ - const Range range; + Range range; - private: - ScopedPointer font; - ScopedPointer colour; + /** The font for this range of characters. */ + Font font; - friend class AttributedString; - Attribute (const Attribute&, int); - Attribute& operator= (const Attribute&); + /** The colour for this range of characters. */ + Colour colour; + private: JUCE_LEAK_DETECTOR (Attribute) }; @@ -189,7 +181,7 @@ public: /** Returns one of the string's attributes. The index provided must be less than getNumAttributes(), and >= 0. */ - const Attribute* getAttribute (int index) const noexcept { return attributes.getUnchecked (index); } + const Attribute& getAttribute (int index) const noexcept { return attributes.getReference (index); } //============================================================================== /** Adds a colour attribute for the specified range. */ @@ -210,7 +202,7 @@ private: Justification justification; WordWrap wordWrap; ReadingDirection readingDirection; - OwnedArray attributes; + Array attributes; JUCE_LEAK_DETECTOR (AttributedString) }; diff --git a/source/modules/juce_graphics/fonts/juce_Font.cpp b/source/modules/juce_graphics/fonts/juce_Font.cpp index f47e182cd..7b7632fb8 100644 --- a/source/modules/juce_graphics/fonts/juce_Font.cpp +++ b/source/modules/juce_graphics/fonts/juce_Font.cpp @@ -159,9 +159,7 @@ void Typeface::setTypefaceCacheSize (int numFontsToCache) TypefaceCache::getInstance()->setSize (numFontsToCache); } -#if JUCE_MODULE_AVAILABLE_juce_opengl -extern void clearOpenGLGlyphCache(); -#endif +void (*clearOpenGLGlyphCache)() = nullptr; void Typeface::clearTypefaceCache() { @@ -169,9 +167,8 @@ void Typeface::clearTypefaceCache() RenderingHelpers::SoftwareRendererSavedState::clearGlyphCache(); - #if JUCE_MODULE_AVAILABLE_juce_opengl - clearOpenGLGlyphCache(); - #endif + if (clearOpenGLGlyphCache != nullptr) + clearOpenGLGlyphCache(); } //============================================================================== diff --git a/source/modules/juce_graphics/fonts/juce_TextLayout.cpp b/source/modules/juce_graphics/fonts/juce_TextLayout.cpp index 322b8e6e0..2418eefc1 100644 --- a/source/modules/juce_graphics/fonts/juce_TextLayout.cpp +++ b/source/modules/juce_graphics/fonts/juce_TextLayout.cpp @@ -287,29 +287,6 @@ void TextLayout::createLayoutWithBalancedLineLengths (const AttributedString& te //============================================================================== namespace TextLayoutHelpers { - struct FontAndColour - { - FontAndColour (const Font* f) noexcept : font (f), colour (0xff000000) {} - - const Font* font; - Colour colour; - - bool operator!= (const FontAndColour& other) const noexcept - { - return (font != other.font && *font != *other.font) || colour != other.colour; - } - }; - - struct RunAttribute - { - RunAttribute (const FontAndColour& fc, const Range r) noexcept - : fontAndColour (fc), range (r) - {} - - FontAndColour fontAndColour; - Range range; - }; - struct Token { Token (const String& t, const Font& f, Colour c, const bool whitespace) @@ -331,18 +308,16 @@ namespace TextLayoutHelpers Token& operator= (const Token&); }; - class TokenList + struct TokenList { - public: TokenList() noexcept : totalLines (0) {} void createLayout (const AttributedString& text, TextLayout& layout) { - tokens.ensureStorageAllocated (64); layout.ensureStorageAllocated (totalLines); addTextRuns (text); - layoutRuns (layout.getWidth()); + layoutRuns (layout.getWidth(), text.getLineSpacing()); int charPosition = 0; int lineStartPosition = 0; @@ -466,10 +441,8 @@ namespace TextLayoutHelpers return CharacterFunctions::isWhitespace (c) ? 2 : 1; } - void appendText (const AttributedString& text, const Range stringRange, - const Font& font, Colour colour) + void appendText (const String& stringText, const Font& font, Colour colour) { - const String stringText (text.getText().substring (stringRange.getStart(), stringRange.getEnd())); String::CharPointerType t (stringText.getCharPointer()); String currentString; int lastCharType = 0; @@ -505,7 +478,7 @@ namespace TextLayoutHelpers tokens.add (new Token (currentString, font, colour, lastCharType == 2)); } - void layoutRuns (const float maxWidth) + void layoutRuns (const float maxWidth, const float extraLineSpacing) { float x = 0, y = 0, h = 0; int i; @@ -516,7 +489,7 @@ namespace TextLayoutHelpers t.area.setPosition (x, y); t.line = totalLines; x += t.area.getWidth(); - h = jmax (h, t.area.getHeight()); + h = jmax (h, t.area.getHeight() + extraLineSpacing); const Token* const nextTok = tokens[i + 1]; @@ -552,48 +525,15 @@ namespace TextLayoutHelpers void addTextRuns (const AttributedString& text) { - Font defaultFont; - Array runAttributes; + const int numAttributes = text.getNumAttributes(); + tokens.ensureStorageAllocated (jmax (64, numAttributes)); + for (int i = 0; i < numAttributes; ++i) { - const int stringLength = text.getText().length(); - int rangeStart = 0; - FontAndColour lastFontAndColour (&defaultFont); - - // Iterate through every character in the string - for (int i = 0; i < stringLength; ++i) - { - FontAndColour newFontAndColour (&defaultFont); - const int numCharacterAttributes = text.getNumAttributes(); - - for (int j = 0; j < numCharacterAttributes; ++j) - { - const AttributedString::Attribute& attr = *text.getAttribute (j); + const AttributedString::Attribute& attr = text.getAttribute (i); - if (attr.range.contains (i)) - { - if (const Font* f = attr.getFont()) newFontAndColour.font = f; - if (const Colour* c = attr.getColour()) newFontAndColour.colour = *c; - } - } - - if (i > 0 && newFontAndColour != lastFontAndColour) - { - runAttributes.add (RunAttribute (lastFontAndColour, Range (rangeStart, i))); - rangeStart = i; - } - - lastFontAndColour = newFontAndColour; - } - - if (rangeStart < stringLength) - runAttributes.add (RunAttribute (lastFontAndColour, Range (rangeStart, stringLength))); - } - - for (int i = 0; i < runAttributes.size(); ++i) - { - const RunAttribute& r = runAttributes.getReference(i); - appendText (text, r.range, *(r.fontAndColour.font), r.fontAndColour.colour); + appendText (text.getText().substring (attr.range.getStart(), attr.range.getEnd()), + attr.font, attr.colour); } } diff --git a/source/modules/juce_graphics/fonts/juce_Typeface.h b/source/modules/juce_graphics/fonts/juce_Typeface.h index 064630554..e39fa6761 100644 --- a/source/modules/juce_graphics/fonts/juce_Typeface.h +++ b/source/modules/juce_graphics/fonts/juce_Typeface.h @@ -46,7 +46,7 @@ class JUCE_API Typeface : public ReferenceCountedObject public: //============================================================================== /** A handy typedef for a pointer to a typeface. */ - typedef ReferenceCountedObjectPtr Ptr; + typedef ReferenceCountedObjectPtr Ptr; //============================================================================== /** Returns the font family of the typeface. diff --git a/source/modules/juce_graphics/geometry/juce_AffineTransform.cpp b/source/modules/juce_graphics/geometry/juce_AffineTransform.cpp index 3f17bb920..a4c1a7e49 100644 --- a/source/modules/juce_graphics/geometry/juce_AffineTransform.cpp +++ b/source/modules/juce_graphics/geometry/juce_AffineTransform.cpp @@ -219,11 +219,9 @@ AffineTransform AffineTransform::inverted() const noexcept return AffineTransform (dst00, dst01, -mat02 * dst00 - mat12 * dst01, dst10, dst11, -mat02 * dst10 - mat12 * dst11); } - else - { - // singularity.. - return *this; - } + + // singularity.. + return *this; } bool AffineTransform::isSingularity() const noexcept diff --git a/source/modules/juce_graphics/geometry/juce_AffineTransform.h b/source/modules/juce_graphics/geometry/juce_AffineTransform.h index f9773f392..77bb8ab7b 100644 --- a/source/modules/juce_graphics/geometry/juce_AffineTransform.h +++ b/source/modules/juce_graphics/geometry/juce_AffineTransform.h @@ -71,8 +71,8 @@ public: transformations to. e.g. @code - AffineTransform myTransform = AffineTransform::identity.rotated (.5f) - .scaled (2.0f); + AffineTransform myTransform = AffineTransform().rotated (.5f) + .scaled (2.0f); @endcode */ static const AffineTransform identity; diff --git a/source/modules/juce_graphics/geometry/juce_Path.cpp b/source/modules/juce_graphics/geometry/juce_Path.cpp index 48c78aaf5..7eecd6621 100644 --- a/source/modules/juce_graphics/geometry/juce_Path.cpp +++ b/source/modules/juce_graphics/geometry/juce_Path.cpp @@ -156,7 +156,7 @@ Path& Path::operator= (const Path& other) #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Path::Path (Path&& other) noexcept - : data (static_cast &&> (other.data)), + : data (static_cast&&> (other.data)), numElements (other.numElements), bounds (other.bounds), useNonZeroWinding (other.useNonZeroWinding) @@ -165,7 +165,7 @@ Path::Path (Path&& other) noexcept Path& Path::operator= (Path&& other) noexcept { - data = static_cast &&> (other.data); + data = static_cast&&> (other.data); numElements = other.numElements; bounds = other.bounds; useNonZeroWinding = other.useNonZeroWinding; @@ -981,7 +981,7 @@ AffineTransform Path::getTransformToScaleToFit (const float x, const float y, if (preserveProportions) { if (w <= 0 || h <= 0 || boundsRect.isEmpty()) - return AffineTransform::identity; + return AffineTransform(); float newW, newH; const float srcRatio = boundsRect.getHeight() / boundsRect.getWidth(); @@ -1030,7 +1030,7 @@ bool Path::contains (const float x, const float y, const float tolerance) const || y <= bounds.pathYMin || y >= bounds.pathYMax) return false; - PathFlatteningIterator i (*this, AffineTransform::identity, tolerance); + PathFlatteningIterator i (*this, AffineTransform(), tolerance); int positiveCrossings = 0; int negativeCrossings = 0; @@ -1062,7 +1062,7 @@ bool Path::contains (const Point point, const float tolerance) const bool Path::intersectsLine (const Line& line, const float tolerance) { - PathFlatteningIterator i (*this, AffineTransform::identity, tolerance); + PathFlatteningIterator i (*this, AffineTransform(), tolerance); Point intersection; while (i.next()) @@ -1085,7 +1085,7 @@ Line Path::getClippedLine (const Line& line, const bool keepSectio } else { - PathFlatteningIterator i (*this, AffineTransform::identity); + PathFlatteningIterator i (*this, AffineTransform()); Point intersection; while (i.next()) diff --git a/source/modules/juce_graphics/geometry/juce_Path.h b/source/modules/juce_graphics/geometry/juce_Path.h index 50d2e0f0c..215314f2d 100644 --- a/source/modules/juce_graphics/geometry/juce_Path.h +++ b/source/modules/juce_graphics/geometry/juce_Path.h @@ -158,7 +158,7 @@ public: /** Returns the length of the path. @see getPointAlongPath */ - float getLength (const AffineTransform& transform = AffineTransform::identity) const; + float getLength (const AffineTransform& transform = AffineTransform()) const; /** Returns a point that is the specified distance along the path. If the distance is greater than the total length of the path, this will return the @@ -166,7 +166,7 @@ public: @see getLength */ Point getPointAlongPath (float distanceFromStart, - const AffineTransform& transform = AffineTransform::identity) const; + const AffineTransform& transform = AffineTransform()) const; /** Finds the point along the path which is nearest to a given position. This sets pointOnPath to the nearest point, and returns the distance of this point from the start @@ -174,7 +174,7 @@ public: */ float getNearestPoint (const Point targetPoint, Point& pointOnPath, - const AffineTransform& transform = AffineTransform::identity) const; + const AffineTransform& transform = AffineTransform()) const; //============================================================================== /** Removes all lines and curves, resetting the path completely. */ @@ -315,8 +315,8 @@ public: template void addRectangle (const Rectangle& rectangle) { - addRectangle (static_cast (rectangle.getX()), static_cast (rectangle.getY()), - static_cast (rectangle.getWidth()), static_cast (rectangle.getHeight())); + addRectangle (static_cast (rectangle.getX()), static_cast (rectangle.getY()), + static_cast (rectangle.getWidth()), static_cast (rectangle.getHeight())); } /** Adds a rectangle with rounded corners to the path. @@ -350,8 +350,8 @@ public: template void addRoundedRectangle (const Rectangle& rectangle, float cornerSizeX, float cornerSizeY) { - addRoundedRectangle (static_cast (rectangle.getX()), static_cast (rectangle.getY()), - static_cast (rectangle.getWidth()), static_cast (rectangle.getHeight()), + addRoundedRectangle (static_cast (rectangle.getX()), static_cast (rectangle.getY()), + static_cast (rectangle.getWidth()), static_cast (rectangle.getHeight()), cornerSizeX, cornerSizeY); } diff --git a/source/modules/juce_graphics/geometry/juce_PathIterator.h b/source/modules/juce_graphics/geometry/juce_PathIterator.h index 0c1e5a163..6994e28c3 100644 --- a/source/modules/juce_graphics/geometry/juce_PathIterator.h +++ b/source/modules/juce_graphics/geometry/juce_PathIterator.h @@ -52,7 +52,7 @@ public: less lines, so can be generated faster, but will be less smooth. */ PathFlatteningIterator (const Path& path, - const AffineTransform& transform = AffineTransform::identity, + const AffineTransform& transform = AffineTransform(), float tolerance = defaultTolerance); /** Destructor. */ @@ -102,7 +102,7 @@ private: float subPathCloseX, subPathCloseY; const bool isIdentityTransform; - HeapBlock stackBase; + HeapBlock stackBase; float* stackPos; size_t index, stackSize; diff --git a/source/modules/juce_graphics/geometry/juce_PathStrokeType.cpp b/source/modules/juce_graphics/geometry/juce_PathStrokeType.cpp index a762aab4d..dbb5cb944 100644 --- a/source/modules/juce_graphics/geometry/juce_PathStrokeType.cpp +++ b/source/modules/juce_graphics/geometry/juce_PathStrokeType.cpp @@ -695,7 +695,7 @@ void PathStrokeType::createDashedStroke (Path& destPath, if (isSolid && ! first) newDestPath.lineTo (it.x2, it.y2); - createStrokedPath (destPath, newDestPath, AffineTransform::identity, extraAccuracy); + createStrokedPath (destPath, newDestPath, AffineTransform(), extraAccuracy); return; } diff --git a/source/modules/juce_graphics/geometry/juce_PathStrokeType.h b/source/modules/juce_graphics/geometry/juce_PathStrokeType.h index b390838fc..2f17bc5bf 100644 --- a/source/modules/juce_graphics/geometry/juce_PathStrokeType.h +++ b/source/modules/juce_graphics/geometry/juce_PathStrokeType.h @@ -104,7 +104,7 @@ public: */ void createStrokedPath (Path& destPath, const Path& sourcePath, - const AffineTransform& transform = AffineTransform::identity, + const AffineTransform& transform = AffineTransform(), float extraAccuracy = 1.0f) const; @@ -136,7 +136,7 @@ public: const Path& sourcePath, const float* dashLengths, int numDashLengths, - const AffineTransform& transform = AffineTransform::identity, + const AffineTransform& transform = AffineTransform(), float extraAccuracy = 1.0f) const; //============================================================================== @@ -163,7 +163,7 @@ public: const Path& sourcePath, float arrowheadStartWidth, float arrowheadStartLength, float arrowheadEndWidth, float arrowheadEndLength, - const AffineTransform& transform = AffineTransform::identity, + const AffineTransform& transform = AffineTransform(), float extraAccuracy = 1.0f) const; //============================================================================== diff --git a/source/modules/juce_graphics/geometry/juce_Rectangle.h b/source/modules/juce_graphics/geometry/juce_Rectangle.h index ac9df0eb3..b3b74d06c 100644 --- a/source/modules/juce_graphics/geometry/juce_Rectangle.h +++ b/source/modules/juce_graphics/geometry/juce_Rectangle.h @@ -578,8 +578,8 @@ public: */ Point getRelativePoint (double relativeX, double relativeY) const noexcept { - return Point (pos.x + static_cast (w * relativeX), - pos.y + static_cast (h * relativeY)); + return Point (pos.x + static_cast (w * relativeX), + pos.y + static_cast (h * relativeY)); } /** Returns true if any part of another rectangle overlaps this one. */ @@ -914,8 +914,8 @@ private: Point pos; ValueType w, h; - static int parseIntAfterSpace (StringRef s) noexcept - { return s.text.findEndOfWhitespace().getIntValue32(); } + static ValueType parseIntAfterSpace (StringRef s) noexcept + { return static_cast (s.text.findEndOfWhitespace().getIntValue32()); } void copyWithRounding (Rectangle& result) const noexcept { result = getSmallestIntegerContainer(); } void copyWithRounding (Rectangle& result) const noexcept { result = toFloat(); } diff --git a/source/modules/juce_graphics/image_formats/juce_GIFLoader.cpp b/source/modules/juce_graphics/image_formats/juce_GIFLoader.cpp index ff5cd8ff2..e0c5b70ab 100644 --- a/source/modules/juce_graphics/image_formats/juce_GIFLoader.cpp +++ b/source/modules/juce_graphics/image_formats/juce_GIFLoader.cpp @@ -432,7 +432,7 @@ Image GIFImageFormat::decodeImage (InputStream& in) #if (JUCE_MAC || JUCE_IOS) && USE_COREGRAPHICS_RENDERING && JUCE_USE_COREIMAGE_LOADER return juce_loadWithCoreImage (in); #else - const ScopedPointer loader (new GIFLoader (in)); + const ScopedPointer loader (new GIFLoader (in)); return loader->image; #endif } diff --git a/source/modules/juce_graphics/image_formats/juce_JPEGLoader.cpp b/source/modules/juce_graphics/image_formats/juce_JPEGLoader.cpp index a5a09a590..ca01ff058 100644 --- a/source/modules/juce_graphics/image_formats/juce_JPEGLoader.cpp +++ b/source/modules/juce_graphics/image_formats/juce_JPEGLoader.cpp @@ -186,7 +186,7 @@ namespace JPEGHelpers static void jpegWriteTerminate (j_compress_ptr cinfo) { - JuceJpegDest* const dest = static_cast (cinfo->dest); + JuceJpegDest* const dest = static_cast (cinfo->dest); const size_t numToWrite = jpegBufferSize - dest->free_in_buffer; dest->output->write (dest->buffer, numToWrite); @@ -194,11 +194,11 @@ namespace JPEGHelpers static boolean jpegWriteFlush (j_compress_ptr cinfo) { - JuceJpegDest* const dest = static_cast (cinfo->dest); + JuceJpegDest* const dest = static_cast (cinfo->dest); const int numToWrite = jpegBufferSize; - dest->next_output_byte = reinterpret_cast (dest->buffer); + dest->next_output_byte = reinterpret_cast (dest->buffer); dest->free_in_buffer = jpegBufferSize; return (boolean) dest->output->write (dest->buffer, (size_t) numToWrite); @@ -271,7 +271,7 @@ Image JPEGImageFormat::decodeImage (InputStream& in) jpegDecompStruct.src->resync_to_restart = jpeg_resync_to_restart; jpegDecompStruct.src->term_source = dummyCallback1; - jpegDecompStruct.src->next_input_byte = static_cast (mb.getData()); + jpegDecompStruct.src->next_input_byte = static_cast (mb.getData()); jpegDecompStruct.src->bytes_in_buffer = mb.getDataSize(); jpeg_read_header (&jpegDecompStruct, TRUE); @@ -363,7 +363,7 @@ bool JPEGImageFormat::writeImageToStream (const Image& image, OutputStream& out) jpegCompStruct.dest = &dest; dest.output = &out; - HeapBlock tempBuffer (jpegBufferSize); + HeapBlock tempBuffer (jpegBufferSize); dest.buffer = tempBuffer; dest.next_output_byte = (JOCTET*) dest.buffer; dest.free_in_buffer = jpegBufferSize; diff --git a/source/modules/juce_graphics/image_formats/juce_PNGLoader.cpp b/source/modules/juce_graphics/image_formats/juce_PNGLoader.cpp index 393bef47a..efb61a778 100644 --- a/source/modules/juce_graphics/image_formats/juce_PNGLoader.cpp +++ b/source/modules/juce_graphics/image_formats/juce_PNGLoader.cpp @@ -63,6 +63,7 @@ namespace pnglibNamespace #pragma clang diagnostic ignored "-Wsign-conversion" #endif + #undef check using std::abs; #define NO_DUMMY_DECL #define PNGLCONF_H 1 diff --git a/source/modules/juce_graphics/images/juce_Image.cpp b/source/modules/juce_graphics/images/juce_Image.cpp index 42998526d..d801b48ce 100644 --- a/source/modules/juce_graphics/images/juce_Image.cpp +++ b/source/modules/juce_graphics/images/juce_Image.cpp @@ -238,13 +238,13 @@ Image& Image::operator= (const Image& other) #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Image::Image (Image&& other) noexcept - : image (static_cast (other.image)) + : image (static_cast (other.image)) { } Image& Image::operator= (Image&& other) noexcept { - image = static_cast (other.image); + image = static_cast (other.image); return *this; } #endif diff --git a/source/modules/juce_graphics/images/juce_ImageConvolutionKernel.h b/source/modules/juce_graphics/images/juce_ImageConvolutionKernel.h index 67d55d61c..43afc9e00 100644 --- a/source/modules/juce_graphics/images/juce_ImageConvolutionKernel.h +++ b/source/modules/juce_graphics/images/juce_ImageConvolutionKernel.h @@ -101,7 +101,7 @@ public: private: //============================================================================== - HeapBlock values; + HeapBlock values; const int size; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImageConvolutionKernel) diff --git a/source/modules/juce_graphics/juce_graphics.cpp b/source/modules/juce_graphics/juce_graphics.cpp index 2d5a0966e..4c0b2e0aa 100644 --- a/source/modules/juce_graphics/juce_graphics.cpp +++ b/source/modules/juce_graphics/juce_graphics.cpp @@ -22,7 +22,7 @@ ============================================================================== */ -#if defined (JUCE_GRAPHICS_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE +#ifdef JUCE_GRAPHICS_H_INCLUDED /* 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 @@ -31,10 +31,6 @@ #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_graphics.h" diff --git a/source/modules/juce_graphics/juce_graphics.h b/source/modules/juce_graphics/juce_graphics.h index a79213188..3aa7a16e5 100644 --- a/source/modules/juce_graphics/juce_graphics.h +++ b/source/modules/juce_graphics/juce_graphics.h @@ -91,9 +91,9 @@ class LowLevelGraphicsContext; #include "images/juce_ImageCache.h" #include "images/juce_ImageConvolutionKernel.h" #include "images/juce_ImageFileFormat.h" -#include "fonts/juce_AttributedString.h" #include "fonts/juce_Typeface.h" #include "fonts/juce_Font.h" +#include "fonts/juce_AttributedString.h" #include "fonts/juce_GlyphArrangement.h" #include "fonts/juce_TextLayout.h" #include "fonts/juce_CustomTypeface.h" diff --git a/source/modules/juce_graphics/native/juce_RenderingHelpers.h b/source/modules/juce_graphics/native/juce_RenderingHelpers.h index cd8cd368d..1930f1167 100644 --- a/source/modules/juce_graphics/native/juce_RenderingHelpers.h +++ b/source/modules/juce_graphics/native/juce_RenderingHelpers.h @@ -2044,7 +2044,7 @@ public: { Path p; p.addRectangle (r); - clipToPath (p, AffineTransform::identity); + clipToPath (p, AffineTransform()); } } @@ -2074,7 +2074,7 @@ public: } else { - clipToPath (r.toPath(), AffineTransform::identity); + clipToPath (r.toPath(), AffineTransform()); } } @@ -2112,7 +2112,7 @@ public: p.applyTransform (transform.complexTransform); p.addRectangle (clip->getClipBounds().toFloat()); p.setUsingNonZeroWinding (false); - clip = clip->clipToPath (p, AffineTransform::identity); + clip = clip->clipToPath (p, AffineTransform()); } } @@ -2205,7 +2205,7 @@ public: { Path p; p.addRectangle (r); - fillPath (p, AffineTransform::identity); + fillPath (p, AffineTransform()); } void fillRect (const Rectangle& r, const bool replaceContents) @@ -2258,7 +2258,7 @@ public: } else { - fillPath (list.toPath(), AffineTransform::identity); + fillPath (list.toPath(), AffineTransform()); } } } @@ -2266,7 +2266,13 @@ public: void fillPath (const Path& path, const AffineTransform& t) { if (clip != nullptr) - fillShape (new EdgeTableRegionType (clip->getClipBounds(), path, transform.getTransformWith (t)), false); + { + const AffineTransform trans (transform.getTransformWith (t)); + const Rectangle clipRect (clip->getClipBounds()); + + if (path.getBoundsTransformed (trans).getSmallestIntegerContainer().intersects (clipRect)) + fillShape (new EdgeTableRegionType (clipRect, path, trans), false); + } } void fillEdgeTable (const EdgeTable& edgeTable, const float x, const int y) @@ -2292,7 +2298,7 @@ public: { Path p; p.addLineSegment (line, 1.0f); - fillPath (p, AffineTransform::identity); + fillPath (p, AffineTransform()); } void drawImage (const Image& sourceImage, const AffineTransform& trans) @@ -2388,7 +2394,7 @@ public: // If our translation doesn't involve any distortion, we can speed it up.. g2.point1.applyTransform (t); g2.point2.applyTransform (t); - t = AffineTransform::identity; + t = AffineTransform(); } shapeToFill->fillAllWithGradient (getThis(), g2, t, isIdentity); diff --git a/source/modules/juce_graphics/native/juce_freetype_Fonts.cpp b/source/modules/juce_graphics/native/juce_freetype_Fonts.cpp index cc87924cd..04a05ddab 100644 --- a/source/modules/juce_graphics/native/juce_freetype_Fonts.cpp +++ b/source/modules/juce_graphics/native/juce_freetype_Fonts.cpp @@ -41,7 +41,7 @@ struct FTLibWrapper : public ReferenceCountedObject FT_Library library; - typedef ReferenceCountedObjectPtr Ptr; + typedef ReferenceCountedObjectPtr Ptr; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FTLibWrapper) }; diff --git a/source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm b/source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm index a6f98741a..2aa0ffa05 100644 --- a/source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm +++ b/source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm @@ -405,19 +405,7 @@ void CoreGraphicsContext::fillCGRect (const CGRect& cgRect, const bool replaceEx { if (replaceExistingContents) { - #if JUCE_IOS CGContextSetBlendMode (context, kCGBlendModeCopy); - #elif MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 - CGContextClearRect (context, cgRect); - #else - #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 - if (CGContextDrawLinearGradient == 0) // (just a way of checking whether we're running in 10.5 or later) - CGContextClearRect (context, cgRect); - else - #endif - CGContextSetBlendMode (context, kCGBlendModeCopy); - #endif - fillCGRect (cgRect, false); CGContextSetBlendMode (context, kCGBlendModeNormal); } @@ -500,15 +488,15 @@ void CoreGraphicsContext::drawImage (const Image& sourceImage, const AffineTrans #if JUCE_IOS CGContextDrawTiledImage (context, imageRect, image); #else - #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 // There's a bug in CGContextDrawTiledImage that makes it incredibly slow // if it's doing a transformation - it's quicker to just draw lots of images manually if (&CGContextDrawTiledImage != 0 && transform.isOnlyTranslation()) + { CGContextDrawTiledImage (context, imageRect, image); + } else - #endif { - // Fallback to manually doing a tiled fill on 10.4 + // Fallback to manually doing a tiled fill CGRect clip = CGRectIntegral (CGContextGetClipBoundingBox (context)); int x = 0, y = 0; @@ -557,7 +545,7 @@ void CoreGraphicsContext::drawLine (const Line& line) { Path p; p.addLineSegment (line, 1.0f); - fillPath (p, AffineTransform::identity); + fillPath (p, AffineTransform()); } } @@ -668,7 +656,7 @@ bool CoreGraphicsContext::drawTextLayout (const AttributedString& text, const Re CoreTextTypeLayout::drawToCGContext (text, area, context, (float) flipHeight); return true; #else - (void) text; (void) area; + ignoreUnused (text, area); return false; #endif } diff --git a/source/modules/juce_graphics/native/juce_mac_Fonts.mm b/source/modules/juce_graphics/native/juce_mac_Fonts.mm index 895d0ee76..dcff0be5e 100644 --- a/source/modules/juce_graphics/native/juce_mac_Fonts.mm +++ b/source/modules/juce_graphics/native/juce_mac_Fonts.mm @@ -22,8 +22,7 @@ ============================================================================== */ -#if (! defined (JUCE_CORETEXT_AVAILABLE)) \ - && (JUCE_IOS || (JUCE_MAC && MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4)) +#ifndef JUCE_CORETEXT_AVAILABLE #define JUCE_CORETEXT_AVAILABLE 1 #endif @@ -220,17 +219,20 @@ namespace CoreTextTypeLayout CGColorSpaceRef rgbColourSpace = CGColorSpaceCreateDeviceRGB(); #endif - CFStringRef cfText = text.getText().toCFString(); CFMutableAttributedStringRef attribString = CFAttributedStringCreateMutable (kCFAllocatorDefault, 0); - CFAttributedStringReplaceString (attribString, CFRangeMake (0, 0), cfText); - CFRelease (cfText); + + if (CFStringRef cfText = text.getText().toCFString()) + { + CFAttributedStringReplaceString (attribString, CFRangeMake (0, 0), cfText); + CFRelease (cfText); + } const int numCharacterAttributes = text.getNumAttributes(); const CFIndex attribStringLen = CFAttributedStringGetLength (attribString); for (int i = 0; i < numCharacterAttributes; ++i) { - const AttributedString::Attribute& attr = *text.getAttribute (i); + const AttributedString::Attribute& attr = text.getAttribute (i); const int rangeStart = attr.range.getStart(); if (rangeStart >= attribStringLen) @@ -238,42 +240,40 @@ namespace CoreTextTypeLayout CFRange range = CFRangeMake (rangeStart, jmin (attr.range.getEnd(), (int) attribStringLen) - rangeStart); - if (const Font* const f = attr.getFont()) + if (CTFontRef ctFontRef = getOrCreateFont (attr.font)) { - if (CTFontRef ctFontRef = getOrCreateFont (*f)) - { - ctFontRef = getFontWithPointSize (ctFontRef, f->getHeight() * getHeightToPointsFactor (ctFontRef)); + ctFontRef = getFontWithPointSize (ctFontRef, attr.font.getHeight() * getHeightToPointsFactor (ctFontRef)); - CFAttributedStringSetAttribute (attribString, range, kCTFontAttributeName, ctFontRef); + CFAttributedStringSetAttribute (attribString, range, kCTFontAttributeName, ctFontRef); - float extraKerning = f->getExtraKerningFactor(); + float extraKerning = attr.font.getExtraKerningFactor(); - if (extraKerning != 0.0f) - { - extraKerning *= f->getHeight(); - - CFNumberRef numberRef = CFNumberCreate (0, kCFNumberFloatType, &extraKerning); - CFAttributedStringSetAttribute (attribString, range, kCTKernAttributeName, numberRef); - CFRelease (numberRef); - } + if (extraKerning != 0.0f) + { + extraKerning *= attr.font.getHeight(); - CFRelease (ctFontRef); + CFNumberRef numberRef = CFNumberCreate (0, kCFNumberFloatType, &extraKerning); + CFAttributedStringSetAttribute (attribString, range, kCTKernAttributeName, numberRef); + CFRelease (numberRef); } + + CFRelease (ctFontRef); } - if (const Colour* const col = attr.getColour()) { + const Colour col (attr.colour); + #if JUCE_IOS - const CGFloat components[] = { col->getFloatRed(), - col->getFloatGreen(), - col->getFloatBlue(), - col->getFloatAlpha() }; + const CGFloat components[] = { col.getFloatRed(), + col.getFloatGreen(), + col.getFloatBlue(), + col.getFloatAlpha() }; CGColorRef colour = CGColorCreate (rgbColourSpace, components); #else - CGColorRef colour = CGColorCreateGenericRGB (col->getFloatRed(), - col->getFloatGreen(), - col->getFloatBlue(), - col->getFloatAlpha()); + CGColorRef colour = CGColorCreateGenericRGB (col.getFloatRed(), + col.getFloatGreen(), + col.getFloatBlue(), + col.getFloatAlpha()); #endif CFAttributedStringSetAttribute (attribString, range, kCTForegroundColorAttributeName, colour); @@ -450,7 +450,7 @@ namespace CoreTextTypeLayout CFDictionaryRef runAttributes = CTRunGetAttributes (run); CTFontRef ctRunFont; - if (CFDictionaryGetValueIfPresent (runAttributes, kCTFontAttributeName, (const void **) &ctRunFont)) + if (CFDictionaryGetValueIfPresent (runAttributes, kCTFontAttributeName, (const void**) &ctRunFont)) { CFStringRef cfsFontName = CTFontCopyPostScriptName (ctRunFont); CTFontRef ctFontRef = CTFontCreateWithName (cfsFontName, referenceFontSize, nullptr); @@ -572,7 +572,7 @@ public: ascent = ctAscent / ctTotalHeight; unitsToHeightScaleFactor = 1.0f / ctTotalHeight; - pathTransform = AffineTransform::identity.scale (unitsToHeightScaleFactor); + pathTransform = AffineTransform::scale (unitsToHeightScaleFactor); fontHeightToPointsFactor = referenceFontSize / ctTotalHeight; @@ -635,7 +635,7 @@ public: return x; } - void getGlyphPositions (const String& text, Array & resultGlyphs, Array & xOffsets) override + void getGlyphPositions (const String& text, Array& resultGlyphs, Array& xOffsets) override { xOffsets.add (0); @@ -815,29 +815,6 @@ StringArray Font::findAllTypefaceStyles (const String& family) #else -//============================================================================== -// The stuff that follows is a mash-up that supports pre-OSX 10.5 APIs. -// (Hopefully all of this can be ditched at some point in the future). - -//============================================================================== -#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 - #define SUPPORT_10_4_FONTS 1 - #define NEW_CGFONT_FUNCTIONS_UNAVAILABLE (CGFontCreateWithFontName == 0) - - #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 - #define SUPPORT_ONLY_10_4_FONTS 1 - #endif - - } // (juce namespace) - - @interface NSFont (PrivateHack) - - (NSGlyph) _defaultGlyphForChar: (unichar) theChar; - @end - - namespace juce - { -#endif - //============================================================================== class OSXTypeface : public Typeface { @@ -858,33 +835,20 @@ public: [nsFont retain]; - #if SUPPORT_ONLY_10_4_FONTS - initWithATSFont(); - #else - #if SUPPORT_10_4_FONTS - if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) - { - initWithATSFont(); - } - else - #endif - { - fontRef = CGFontCreateWithFontName ((CFStringRef) [nsFont fontName]); + fontRef = CGFontCreateWithFontName ((CFStringRef) [nsFont fontName]); - const float absAscent = std::abs ((float) CGFontGetAscent (fontRef)); - const float totalHeight = absAscent + std::abs ((float) CGFontGetDescent (fontRef)); + const float absAscent = std::abs ((float) CGFontGetAscent (fontRef)); + const float totalHeight = absAscent + std::abs ((float) CGFontGetDescent (fontRef)); - ascent = absAscent / totalHeight; - unitsToHeightScaleFactor = 1.0f / totalHeight; + ascent = absAscent / totalHeight; + unitsToHeightScaleFactor = 1.0f / totalHeight; - const float nsFontAscent = std::abs ([nsFont ascender]); - const float nsFontDescent = std::abs ([nsFont descender]); + const float nsFontAscent = std::abs ([nsFont ascender]); + const float nsFontDescent = std::abs ([nsFont descender]); - fontHeightToPointsFactor = referenceFontSize / (nsFontAscent + nsFontDescent); - } - #endif + fontHeightToPointsFactor = referenceFontSize / (nsFontAscent + nsFontDescent); - pathTransform = AffineTransform::identity.scale (unitsToHeightScaleFactor); + pathTransform = AffineTransform::scale (unitsToHeightScaleFactor); } } @@ -898,27 +862,6 @@ public: CGFontRelease (fontRef); } - #if SUPPORT_10_4_FONTS - void initWithATSFont() - { - ATSFontRef atsFont = ATSFontFindFromName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault); - - if (atsFont == 0) - atsFont = ATSFontFindFromPostScriptName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault); - - fontRef = CGFontCreateWithPlatformFont (&atsFont); - - const float absAscent = std::abs ([nsFont ascender]); - const float absDescent = std::abs ([nsFont descender]); - const float totalHeight = absAscent + absDescent; - - unitsToHeightScaleFactor = 1.0f / totalHeight; - fontHeightToPointsFactor = referenceFontSize / totalHeight; - ascent = absAscent / totalHeight; - } - #endif - - float getAscent() const override { return ascent; } float getDescent() const override { return 1.0f - ascent; } float getHeightToPointsFactor() const override { return fontHeightToPointsFactor; } @@ -929,37 +872,16 @@ public: return 0; const int length = text.length(); - HeapBlock glyphs; + HeapBlock glyphs; createGlyphsForString (text.getCharPointer(), length, glyphs); float x = 0; -#if SUPPORT_ONLY_10_4_FONTS - HeapBlock advances (length); - [nsFont getAdvancements: advances forGlyphs: reinterpret_cast (glyphs.getData()) count: length]; - - for (int i = 0; i < length; ++i) - x += advances[i].width; -#else - #if SUPPORT_10_4_FONTS - if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) - { - HeapBlock advances (length); - [nsFont getAdvancements: advances forGlyphs: reinterpret_cast (glyphs.getData()) count: length]; + HeapBlock advances (length); + if (CGFontGetGlyphAdvances (fontRef, glyphs, length, advances)) for (int i = 0; i < length; ++i) - x += advances[i].width; - } - else - #endif - { - HeapBlock advances (length); - - if (CGFontGetGlyphAdvances (fontRef, glyphs, length, advances)) - for (int i = 0; i < length; ++i) - x += advances[i]; - } -#endif + x += advances[i]; return x * unitsToHeightScaleFactor; } @@ -972,54 +894,21 @@ public: return; const int length = text.length(); - HeapBlock glyphs; + HeapBlock glyphs; createGlyphsForString (text.getCharPointer(), length, glyphs); -#if SUPPORT_ONLY_10_4_FONTS - HeapBlock advances (length); - [nsFont getAdvancements: advances forGlyphs: reinterpret_cast (glyphs.getData()) count: length]; + HeapBlock advances (length); - int x = 0; - for (int i = 0; i < length; ++i) + if (CGFontGetGlyphAdvances (fontRef, glyphs, length, advances)) { - x += advances[i].width; - xOffsets.add (x * unitsToHeightScaleFactor); - resultGlyphs.add (reinterpret_cast (glyphs.getData())[i]); - } - -#else - #if SUPPORT_10_4_FONTS - if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) - { - HeapBlock advances (length); - NSGlyph* const nsGlyphs = reinterpret_cast (glyphs.getData()); - [nsFont getAdvancements: advances forGlyphs: nsGlyphs count: length]; - - float x = 0; + int x = 0; for (int i = 0; i < length; ++i) { - x += advances[i].width; + x += advances [i]; xOffsets.add (x * unitsToHeightScaleFactor); - resultGlyphs.add (nsGlyphs[i]); + resultGlyphs.add (glyphs[i]); } } - else - #endif - { - HeapBlock advances (length); - - if (CGFontGetGlyphAdvances (fontRef, glyphs, length, advances)) - { - int x = 0; - for (int i = 0; i < length; ++i) - { - x += advances [i]; - xOffsets.add (x * unitsToHeightScaleFactor); - resultGlyphs.add (glyphs[i]); - } - } - } -#endif } bool getOutlineForGlyph (int glyphNumber, Path& path) override @@ -1074,24 +963,8 @@ private: AffineTransform pathTransform; #endif - void createGlyphsForString (String::CharPointerType text, const int length, HeapBlock & glyphs) + void createGlyphsForString (String::CharPointerType text, const int length, HeapBlock& glyphs) { - #if SUPPORT_10_4_FONTS - #if ! SUPPORT_ONLY_10_4_FONTS - if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) - #endif - { - glyphs.malloc (sizeof (NSGlyph) * length, 1); - NSGlyph* const nsGlyphs = reinterpret_cast (glyphs.getData()); - - for (int i = 0; i < length; ++i) - nsGlyphs[i] = (NSGlyph) [nsFont _defaultGlyphForChar: text.getAndAdvance()]; - - return; - } - #endif - - #if ! SUPPORT_ONLY_10_4_FONTS if (charToGlyphMapper == nullptr) charToGlyphMapper = new CharToGlyphMapper (fontRef); @@ -1099,10 +972,8 @@ private: for (int i = 0; i < length; ++i) glyphs[i] = (CGGlyph) charToGlyphMapper->getGlyphForCharacter (text.getAndAdvance()); - #endif } - #if ! SUPPORT_ONLY_10_4_FONTS // Reads a CGFontRef's character map table to convert unicode into glyph numbers class CharToGlyphMapper { @@ -1200,8 +1071,7 @@ private: } }; - ScopedPointer charToGlyphMapper; - #endif + ScopedPointer charToGlyphMapper; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXTypeface) }; @@ -1307,17 +1177,16 @@ static bool canAllTypefacesBeUsedInLayout (const AttributedString& text) for (int i = 0; i < numCharacterAttributes; ++i) { - if (const Font* const f = text.getAttribute (i)->getFont()) + Typeface* t = text.getAttribute(i).font.getTypeface(); + + if (OSXTypeface* tf = dynamic_cast (t)) { - if (OSXTypeface* tf = dynamic_cast (f->getTypeface())) - { - if (tf->isMemoryFont) - return false; - } - else if (dynamic_cast (f->getTypeface()) != nullptr) - { + if (tf->isMemoryFont) return false; - } + } + else if (dynamic_cast (t) != nullptr) + { + return false; } } @@ -1337,6 +1206,6 @@ bool TextLayout::createNativeLayout (const AttributedString& text) } #endif - (void) text; + ignoreUnused (text); return false; } diff --git a/source/modules/juce_graphics/native/juce_win32_Direct2DGraphicsContext.cpp b/source/modules/juce_graphics/native/juce_win32_Direct2DGraphicsContext.cpp index ba1d3397a..b36409236 100644 --- a/source/modules/juce_graphics/native/juce_win32_Direct2DGraphicsContext.cpp +++ b/source/modules/juce_graphics/native/juce_win32_Direct2DGraphicsContext.cpp @@ -40,7 +40,7 @@ public: if (factories->d2dFactory != nullptr) { HRESULT hr = factories->d2dFactory->CreateHwndRenderTarget (props, propsHwnd, renderingTarget.resetAndGetPointerAddress()); - jassert (SUCCEEDED (hr)); (void) hr; + jassert (SUCCEEDED (hr)); ignoreUnused (hr); hr = renderingTarget->CreateSolidColorBrush (D2D1::ColorF::ColorF (0.0f, 0.0f, 0.0f, 1.0f), colourBrush.resetAndGetPointerAddress()); } } @@ -198,7 +198,7 @@ public: void fillPath (const Path& p, const AffineTransform& transform) { currentState->createBrush(); - ComSmartPtr geometry (pathToPathGeometry (p, transform.followedBy (currentState->transform))); + ComSmartPtr geometry (pathToPathGeometry (p, transform.followedBy (currentState->transform))); if (renderingTarget != nullptr) renderingTarget->FillGeometry (geometry, currentState->currentBrush); @@ -220,7 +220,7 @@ public: bp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; { - ComSmartPtr tempBitmap; + ComSmartPtr tempBitmap; renderingTarget->CreateBitmap (size, bd.data, bd.lineStride, bp, tempBitmap.resetAndGetPointerAddress()); if (tempBitmap != nullptr) renderingTarget->DrawBitmap (tempBitmap); @@ -286,7 +286,7 @@ public: { renderingTarget->SetTransform (transformToMatrix (currentState->transform)); - DirectWriteTypeLayout::drawToD2DContext (text, area, renderingTarget, factories->directWriteFactory, + DirectWriteTypeLayout::drawToD2DContext (text, area, *renderingTarget, factories->directWriteFactory, factories->d2dFactory, factories->systemFonts); renderingTarget->SetTransform (D2D1::IdentityMatrix()); @@ -654,38 +654,38 @@ public: Font font; float fontHeightToEmSizeFactor; IDWriteFontFace* currentFontFace; - ComSmartPtr localFontFace; + ComSmartPtr localFontFace; FillType fillType; Image image; - ComSmartPtr bitmap; // xxx needs a better name - what is this for?? + ComSmartPtr bitmap; // xxx needs a better name - what is this for?? Rectangle clipRect; bool clipsRect, shouldClipRect; - ComSmartPtr complexClipGeometry; + ComSmartPtr complexClipGeometry; D2D1_LAYER_PARAMETERS complexClipLayerParams; - ComSmartPtr complexClipLayer; + ComSmartPtr complexClipLayer; bool clipsComplex, shouldClipComplex; - ComSmartPtr rectListGeometry; + ComSmartPtr rectListGeometry; D2D1_LAYER_PARAMETERS rectListLayerParams; - ComSmartPtr rectListLayer; + ComSmartPtr rectListLayer; bool clipsRectList, shouldClipRectList; Image maskImage; D2D1_LAYER_PARAMETERS imageMaskLayerParams; - ComSmartPtr bitmapMaskLayer; - ComSmartPtr maskBitmap; - ComSmartPtr bitmapMaskBrush; + ComSmartPtr bitmapMaskLayer; + ComSmartPtr maskBitmap; + ComSmartPtr bitmapMaskBrush; bool clipsBitmap, shouldClipBitmap; ID2D1Brush* currentBrush; - ComSmartPtr bitmapBrush; - ComSmartPtr linearGradient; - ComSmartPtr radialGradient; - ComSmartPtr gradientStops; + ComSmartPtr bitmapBrush; + ComSmartPtr linearGradient; + ComSmartPtr radialGradient; + ComSmartPtr gradientStops; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SavedState) @@ -695,8 +695,8 @@ public: private: SharedResourcePointer factories; HWND hwnd; - ComSmartPtr renderingTarget; - ComSmartPtr colourBrush; + ComSmartPtr renderingTarget; + ComSmartPtr colourBrush; Rectangle bounds; SavedState* currentState; @@ -734,7 +734,7 @@ private: ID2D1PathGeometry* p = nullptr; factories->d2dFactory->CreatePathGeometry (&p); - ComSmartPtr sink; + ComSmartPtr sink; HRESULT hr = p->Open (sink.resetAndGetPointerAddress()); // xxx handle error sink->SetFillMode (D2D1_FILL_MODE_WINDING); @@ -812,7 +812,7 @@ private: ID2D1PathGeometry* p = nullptr; factories->d2dFactory->CreatePathGeometry (&p); - ComSmartPtr sink; + ComSmartPtr sink; HRESULT hr = p->Open (sink.resetAndGetPointerAddress()); sink->SetFillMode (D2D1_FILL_MODE_WINDING); // xxx need to check Path::isUsingNonZeroWinding() diff --git a/source/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp b/source/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp index d637fca96..e5d47236f 100644 --- a/source/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp +++ b/source/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp @@ -22,14 +22,14 @@ ============================================================================== */ -//================================================================================================== + #if JUCE_USE_DIRECTWRITE namespace DirectWriteTypeLayout { class CustomDirectWriteTextRenderer : public ComBaseClassHelper { public: - CustomDirectWriteTextRenderer (IDWriteFontCollection* const fonts, const AttributedString& as) + CustomDirectWriteTextRenderer (IDWriteFontCollection& fonts, const AttributedString& as) : ComBaseClassHelper (0), attributedString (as), fontCollection (fonts), @@ -38,55 +38,52 @@ namespace DirectWriteTypeLayout { } - JUCE_COMRESULT QueryInterface (REFIID refId, void** result) + JUCE_COMRESULT QueryInterface (REFIID refId, void** result) override { if (refId == __uuidof (IDWritePixelSnapping)) - return castToType (result); + return castToType (result); return ComBaseClassHelper::QueryInterface (refId, result); } - JUCE_COMRESULT IsPixelSnappingDisabled (void* /*clientDrawingContext*/, BOOL* isDisabled) + JUCE_COMRESULT IsPixelSnappingDisabled (void* /*clientDrawingContext*/, BOOL* isDisabled) override { *isDisabled = FALSE; return S_OK; } - JUCE_COMRESULT GetCurrentTransform (void*, DWRITE_MATRIX* matrix) + JUCE_COMRESULT GetCurrentTransform (void*, DWRITE_MATRIX* matrix) override { - matrix->m11 = 1.0f; - matrix->m12 = 0.0f; - matrix->m21 = 0.0f; - matrix->m22 = 1.0f; - matrix->dx = 0.0f; - matrix->dy = 0.0f; + matrix->m11 = 1.0f; matrix->m12 = 0.0f; + matrix->m21 = 0.0f; matrix->m22 = 1.0f; + matrix->dx = 0.0f; matrix->dy = 0.0f; return S_OK; } - JUCE_COMRESULT GetPixelsPerDip (void*, FLOAT* pixelsPerDip) + JUCE_COMRESULT GetPixelsPerDip (void*, FLOAT* pixelsPerDip) override { *pixelsPerDip = 1.0f; return S_OK; } - JUCE_COMRESULT DrawUnderline (void*, FLOAT, FLOAT, DWRITE_UNDERLINE const*, IUnknown*) + JUCE_COMRESULT DrawUnderline (void*, FLOAT, FLOAT, DWRITE_UNDERLINE const*, IUnknown*) override { return E_NOTIMPL; } - JUCE_COMRESULT DrawStrikethrough (void*, FLOAT, FLOAT, DWRITE_STRIKETHROUGH const*, IUnknown*) + JUCE_COMRESULT DrawStrikethrough (void*, FLOAT, FLOAT, DWRITE_STRIKETHROUGH const*, IUnknown*) override { return E_NOTIMPL; } - JUCE_COMRESULT DrawInlineObject (void*, FLOAT, FLOAT, IDWriteInlineObject*, BOOL, BOOL, IUnknown*) + JUCE_COMRESULT DrawInlineObject (void*, FLOAT, FLOAT, IDWriteInlineObject*, BOOL, BOOL, IUnknown*) override { return E_NOTIMPL; } JUCE_COMRESULT DrawGlyphRun (void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, DWRITE_MEASURING_MODE, DWRITE_GLYPH_RUN const* glyphRun, DWRITE_GLYPH_RUN_DESCRIPTION const* runDescription, - IUnknown* clientDrawingEffect) + IUnknown* clientDrawingEffect) override { TextLayout* const layout = static_cast (clientDrawingContext); @@ -113,8 +110,8 @@ namespace DirectWriteTypeLayout DWRITE_FONT_METRICS dwFontMetrics; glyphRun->fontFace->GetMetrics (&dwFontMetrics); - glyphLine.ascent = jmax (glyphLine.ascent, scaledFontSize (dwFontMetrics.ascent, dwFontMetrics, glyphRun)); - glyphLine.descent = jmax (glyphLine.descent, scaledFontSize (dwFontMetrics.descent, dwFontMetrics, glyphRun)); + glyphLine.ascent = jmax (glyphLine.ascent, scaledFontSize (dwFontMetrics.ascent, dwFontMetrics, *glyphRun)); + glyphLine.descent = jmax (glyphLine.descent, scaledFontSize (dwFontMetrics.descent, dwFontMetrics, *glyphRun)); TextLayout::Run* const glyphRunLayout = new TextLayout::Run (Range (runDescription->textPosition, runDescription->textPosition + runDescription->stringLength), @@ -125,7 +122,7 @@ namespace DirectWriteTypeLayout const float totalHeight = std::abs ((float) dwFontMetrics.ascent) + std::abs ((float) dwFontMetrics.descent); const float fontHeightToEmSizeFactor = (float) dwFontMetrics.designUnitsPerEm / totalHeight; - glyphRunLayout->font = getFontForRun (glyphRun, glyphRun->fontEmSize / fontHeightToEmSizeFactor); + glyphRunLayout->font = getFontForRun (*glyphRun, glyphRun->fontEmSize / fontHeightToEmSizeFactor); glyphRunLayout->colour = getColourOf (static_cast (clientDrawingEffect)); const Point lineOrigin (layout->getLine (currentLine).lineOrigin); @@ -151,16 +148,16 @@ namespace DirectWriteTypeLayout private: const AttributedString& attributedString; - IDWriteFontCollection* const fontCollection; + IDWriteFontCollection& fontCollection; int currentLine; float lastOriginY; - static float scaledFontSize (int n, const DWRITE_FONT_METRICS& metrics, const DWRITE_GLYPH_RUN* glyphRun) noexcept + static float scaledFontSize (int n, const DWRITE_FONT_METRICS& metrics, const DWRITE_GLYPH_RUN& glyphRun) noexcept { - return (std::abs ((float) n) / (float) metrics.designUnitsPerEm) * glyphRun->fontEmSize; + return (std::abs ((float) n) / (float) metrics.designUnitsPerEm) * glyphRun.fontEmSize; } - static Colour getColourOf (ID2D1SolidColorBrush* d2dBrush) + static Colour getColourOf (ID2D1SolidColorBrush* d2dBrush) noexcept { if (d2dBrush == nullptr) return Colours::black; @@ -169,16 +166,19 @@ namespace DirectWriteTypeLayout return Colour::fromFloatRGBA (colour.r, colour.g, colour.b, colour.a); } - Font getFontForRun (DWRITE_GLYPH_RUN const* glyphRun, float fontHeight) + Font getFontForRun (const DWRITE_GLYPH_RUN& glyphRun, float fontHeight) { for (int i = 0; i < attributedString.getNumAttributes(); ++i) - if (const Font* font = attributedString.getAttribute(i)->getFont()) - if (WindowsDirectWriteTypeface* wt = dynamic_cast (font->getTypeface())) - if (wt->getIDWriteFontFace() == glyphRun->fontFace) - return font->withHeight (fontHeight); + { + const Font& font = attributedString.getAttribute(i).font; + + if (WindowsDirectWriteTypeface* wt = dynamic_cast (font.getTypeface())) + if (wt->getIDWriteFontFace() == glyphRun.fontFace) + return font.withHeight (fontHeight); + } ComSmartPtr dwFont; - HRESULT hr = fontCollection->GetFontFromFontFace (glyphRun->fontFace, dwFont.resetAndGetPointerAddress()); + HRESULT hr = fontCollection.GetFontFromFontFace (glyphRun.fontFace, dwFont.resetAndGetPointerAddress()); jassert (dwFont != nullptr); ComSmartPtr dwFontFamily; @@ -191,10 +191,10 @@ namespace DirectWriteTypeLayout }; //================================================================================================== - static float getFontHeightToEmSizeFactor (IDWriteFont* const dwFont) + static float getFontHeightToEmSizeFactor (IDWriteFont& dwFont) { ComSmartPtr dwFontFace; - dwFont->CreateFontFace (dwFontFace.resetAndGetPointerAddress()); + dwFont.CreateFontFace (dwFontFace.resetAndGetPointerAddress()); if (dwFontFace == nullptr) return 1.0f; @@ -206,7 +206,7 @@ namespace DirectWriteTypeLayout return dwFontMetrics.designUnitsPerEm / totalHeight; } - void setTextFormatProperties (const AttributedString& text, IDWriteTextFormat* const format) + void setTextFormatProperties (const AttributedString& text, IDWriteTextFormat& format) { DWRITE_TEXT_ALIGNMENT alignment = DWRITE_TEXT_ALIGNMENT_LEADING; DWRITE_WORD_WRAPPING wrapType = DWRITE_WORD_WRAPPING_WRAP; @@ -232,7 +232,7 @@ namespace DirectWriteTypeLayout // This must be set correctly and manually when using RTL Scripts (Hebrew, Arabic) if (text.getReadingDirection() == AttributedString::rightToLeft) { - format->SetReadingDirection (DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); + format.SetReadingDirection (DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); switch (text.getJustification().getOnlyHorizontalFlags()) { @@ -242,31 +242,29 @@ namespace DirectWriteTypeLayout } } - format->SetTextAlignment (alignment); - format->SetWordWrapping (wrapType); + format.SetTextAlignment (alignment); + format.SetWordWrapping (wrapType); } - void addAttributedRange (const AttributedString::Attribute& attr, IDWriteTextLayout* textLayout, - const int textLen, ID2D1RenderTarget* const renderTarget, IDWriteFontCollection* const fontCollection) + void addAttributedRange (const AttributedString::Attribute& attr, IDWriteTextLayout& textLayout, + const int textLen, ID2D1RenderTarget& renderTarget, IDWriteFontCollection& fontCollection) { DWRITE_TEXT_RANGE range; range.startPosition = attr.range.getStart(); range.length = jmin (attr.range.getLength(), textLen - attr.range.getStart()); - if (const Font* const font = attr.getFont()) { - const String familyName (FontStyleHelpers::getConcreteFamilyName (*font)); + const String familyName (FontStyleHelpers::getConcreteFamilyName (attr.font)); BOOL fontFound = false; uint32 fontIndex; - fontCollection->FindFamilyName (familyName.toWideCharPointer(), - &fontIndex, &fontFound); + fontCollection.FindFamilyName (familyName.toWideCharPointer(), &fontIndex, &fontFound); if (! fontFound) fontIndex = 0; ComSmartPtr fontFamily; - HRESULT hr = fontCollection->GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress()); + HRESULT hr = fontCollection.GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress()); ComSmartPtr dwFont; uint32 fontFacesCount = 0; @@ -276,36 +274,36 @@ namespace DirectWriteTypeLayout { hr = fontFamily->GetFont (i, dwFont.resetAndGetPointerAddress()); - if (font->getTypefaceStyle() == getFontFaceName (dwFont)) + if (attr.font.getTypefaceStyle() == getFontFaceName (dwFont)) break; } - textLayout->SetFontFamilyName (familyName.toWideCharPointer(), range); - textLayout->SetFontWeight (dwFont->GetWeight(), range); - textLayout->SetFontStretch (dwFont->GetStretch(), range); - textLayout->SetFontStyle (dwFont->GetStyle(), range); + textLayout.SetFontFamilyName (familyName.toWideCharPointer(), range); + textLayout.SetFontWeight (dwFont->GetWeight(), range); + textLayout.SetFontStretch (dwFont->GetStretch(), range); + textLayout.SetFontStyle (dwFont->GetStyle(), range); - const float fontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (dwFont); - textLayout->SetFontSize (font->getHeight() * fontHeightToEmSizeFactor, range); + const float fontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (*dwFont); + textLayout.SetFontSize (attr.font.getHeight() * fontHeightToEmSizeFactor, range); } - if (const Colour* const colour = attr.getColour()) { + const Colour col (attr.colour); ComSmartPtr d2dBrush; - renderTarget->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF (colour->getFloatRed(), - colour->getFloatGreen(), - colour->getFloatBlue(), - colour->getFloatAlpha())), - d2dBrush.resetAndGetPointerAddress()); + renderTarget.CreateSolidColorBrush (D2D1::ColorF (col.getFloatRed(), + col.getFloatGreen(), + col.getFloatBlue(), + col.getFloatAlpha()), + d2dBrush.resetAndGetPointerAddress()); // We need to call SetDrawingEffect with a legimate brush to get DirectWrite to break text based on colours - textLayout->SetDrawingEffect (d2dBrush, range); + textLayout.SetDrawingEffect (d2dBrush, range); } } bool setupLayout (const AttributedString& text, const float maxWidth, const float maxHeight, - ID2D1RenderTarget* const renderTarget, IDWriteFactory* const directWriteFactory, - IDWriteFontCollection* const fontCollection, ComSmartPtr& textLayout) + ID2D1RenderTarget& renderTarget, IDWriteFactory& directWriteFactory, + IDWriteFontCollection& fontCollection, ComSmartPtr& textLayout) { // To add color to text, we need to create a D2D render target // Since we are not actually rendering to a D2D context we create a temporary GDI render target @@ -313,41 +311,40 @@ namespace DirectWriteTypeLayout Font defaultFont; BOOL fontFound = false; uint32 fontIndex; - fontCollection->FindFamilyName (defaultFont.getTypeface()->getName().toWideCharPointer(), &fontIndex, &fontFound); + fontCollection.FindFamilyName (defaultFont.getTypeface()->getName().toWideCharPointer(), &fontIndex, &fontFound); if (! fontFound) fontIndex = 0; ComSmartPtr dwFontFamily; - HRESULT hr = fontCollection->GetFontFamily (fontIndex, dwFontFamily.resetAndGetPointerAddress()); + HRESULT hr = fontCollection.GetFontFamily (fontIndex, dwFontFamily.resetAndGetPointerAddress()); ComSmartPtr dwFont; hr = dwFontFamily->GetFirstMatchingFont (DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL, dwFont.resetAndGetPointerAddress()); + jassert (dwFont != nullptr); - const float defaultFontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (dwFont); - - jassert (directWriteFactory != nullptr); + const float defaultFontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (*dwFont); ComSmartPtr dwTextFormat; - hr = directWriteFactory->CreateTextFormat (defaultFont.getTypefaceName().toWideCharPointer(), fontCollection, - DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, - defaultFont.getHeight() * defaultFontHeightToEmSizeFactor, - L"en-us", dwTextFormat.resetAndGetPointerAddress()); + hr = directWriteFactory.CreateTextFormat (defaultFont.getTypefaceName().toWideCharPointer(), &fontCollection, + DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, + defaultFont.getHeight() * defaultFontHeightToEmSizeFactor, + L"en-us", dwTextFormat.resetAndGetPointerAddress()); - setTextFormatProperties (text, dwTextFormat); + setTextFormatProperties (text, *dwTextFormat); { DWRITE_TRIMMING trimming = { DWRITE_TRIMMING_GRANULARITY_CHARACTER, 0, 0 }; ComSmartPtr trimmingSign; - hr = directWriteFactory->CreateEllipsisTrimmingSign (dwTextFormat, trimmingSign.resetAndGetPointerAddress()); + hr = directWriteFactory.CreateEllipsisTrimmingSign (dwTextFormat, trimmingSign.resetAndGetPointerAddress()); hr = dwTextFormat->SetTrimming (&trimming, trimmingSign); } const int textLen = text.getText().length(); - hr = directWriteFactory->CreateTextLayout (text.getText().toWideCharPointer(), textLen, dwTextFormat, - maxWidth, maxHeight, textLayout.resetAndGetPointerAddress()); + hr = directWriteFactory.CreateTextLayout (text.getText().toWideCharPointer(), textLen, dwTextFormat, + maxWidth, maxHeight, textLayout.resetAndGetPointerAddress()); if (FAILED (hr) || textLayout == nullptr) return false; @@ -355,26 +352,16 @@ namespace DirectWriteTypeLayout const int numAttributes = text.getNumAttributes(); for (int i = 0; i < numAttributes; ++i) - addAttributedRange (*text.getAttribute (i), textLayout, textLen, renderTarget, fontCollection); + addAttributedRange (text.getAttribute (i), *textLayout, textLen, renderTarget, fontCollection); return true; } - void createLayout (TextLayout& layout, const AttributedString& text, IDWriteFactory* const directWriteFactory, - ID2D1Factory* const direct2dFactory, IDWriteFontCollection* const fontCollection) + void createLayout (TextLayout& layout, const AttributedString& text, + IDWriteFactory& directWriteFactory, + IDWriteFontCollection& fontCollection, + ID2D1DCRenderTarget& renderTarget) { - // To add color to text, we need to create a D2D render target - // Since we are not actually rendering to a D2D context we create a temporary GDI render target - - D2D1_RENDER_TARGET_PROPERTIES d2dRTProp = D2D1::RenderTargetProperties (D2D1_RENDER_TARGET_TYPE_SOFTWARE, - D2D1::PixelFormat (DXGI_FORMAT_B8G8R8A8_UNORM, - D2D1_ALPHA_MODE_IGNORE), - 0, 0, - D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE, - D2D1_FEATURE_LEVEL_DEFAULT); - ComSmartPtr renderTarget; - HRESULT hr = direct2dFactory->CreateDCRenderTarget (&d2dRTProp, renderTarget.resetAndGetPointerAddress()); - ComSmartPtr dwTextLayout; if (! setupLayout (text, layout.getWidth(), layout.getHeight(), renderTarget, @@ -382,7 +369,7 @@ namespace DirectWriteTypeLayout return; UINT32 actualLineCount = 0; - hr = dwTextLayout->GetLineMetrics (nullptr, 0, &actualLineCount); + HRESULT hr = dwTextLayout->GetLineMetrics (nullptr, 0, &actualLineCount); layout.ensureStorageAllocated (actualLineCount); @@ -395,16 +382,21 @@ namespace DirectWriteTypeLayout hr = dwTextLayout->GetLineMetrics (dwLineMetrics, actualLineCount, &actualLineCount); int lastLocation = 0; const int numLines = jmin ((int) actualLineCount, layout.getNumLines()); + float yAdjustment = 0; + const float extraLineSpacing = text.getLineSpacing(); for (int i = 0; i < numLines; ++i) { - layout.getLine(i).stringRange = Range (lastLocation, (int) lastLocation + dwLineMetrics[i].length); + TextLayout::Line& line = layout.getLine (i); + line.stringRange = Range (lastLocation, (int) lastLocation + dwLineMetrics[i].length); + line.lineOrigin.y += yAdjustment; + yAdjustment += extraLineSpacing; lastLocation += dwLineMetrics[i].length; } } - void drawToD2DContext (const AttributedString& text, const Rectangle& area, ID2D1RenderTarget* const renderTarget, - IDWriteFactory* const directWriteFactory, IDWriteFontCollection* const fontCollection) + void drawToD2DContext (const AttributedString& text, const Rectangle& area, ID2D1RenderTarget& renderTarget, + IDWriteFactory& directWriteFactory, IDWriteFontCollection& fontCollection) { ComSmartPtr dwTextLayout; @@ -412,11 +404,11 @@ namespace DirectWriteTypeLayout directWriteFactory, fontCollection, dwTextLayout)) { ComSmartPtr d2dBrush; - renderTarget->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF (0.0f, 0.0f, 0.0f, 1.0f)), - d2dBrush.resetAndGetPointerAddress()); + renderTarget.CreateSolidColorBrush (D2D1::ColorF (0.0f, 0.0f, 0.0f, 1.0f), + d2dBrush.resetAndGetPointerAddress()); - renderTarget->DrawTextLayout (D2D1::Point2F ((float) area.getX(), (float) area.getY()), - dwTextLayout, d2dBrush, D2D1_DRAW_TEXT_OPTIONS_CLIP); + renderTarget.DrawTextLayout (D2D1::Point2F ((float) area.getX(), (float) area.getY()), + dwTextLayout, d2dBrush, D2D1_DRAW_TEXT_OPTIONS_CLIP); } } } @@ -426,9 +418,8 @@ static bool canAllTypefacesBeUsedInLayout (const AttributedString& text) const int numCharacterAttributes = text.getNumAttributes(); for (int i = 0; i < numCharacterAttributes; ++i) - if (const Font* const font = text.getAttribute (i)->getFont()) - if (dynamic_cast (font->getTypeface()) == nullptr) - return false; + if (dynamic_cast (text.getAttribute(i).font.getTypeface()) == nullptr) + return false; return true; } @@ -445,12 +436,15 @@ bool TextLayout::createNativeLayout (const AttributedString& text) if (factories->d2dFactory != nullptr && factories->systemFonts != nullptr) { - DirectWriteTypeLayout::createLayout (*this, text, factories->directWriteFactory, - factories->d2dFactory, factories->systemFonts); + DirectWriteTypeLayout::createLayout (*this, text, + *factories->directWriteFactory, + *factories->systemFonts, + *factories->directWriteRenderTarget); + return true; } #else - (void) text; + ignoreUnused (text); #endif return false; diff --git a/source/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp b/source/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp index dcf660623..66e4d2e25 100644 --- a/source/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp +++ b/source/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp @@ -41,7 +41,7 @@ namespace HeapBlock name (length + 1); hr = names->GetString (index, name, length + 1); - return static_cast (name); + return static_cast (name); } static String getFontFamilyName (IDWriteFontFamily* family) @@ -49,7 +49,7 @@ namespace jassert (family != nullptr); ComSmartPtr familyNames; HRESULT hr = family->GetFamilyNames (familyNames.resetAndGetPointerAddress()); - jassert (SUCCEEDED (hr)); (void) hr; + jassert (SUCCEEDED (hr)); ignoreUnused (hr); return getLocalisedName (familyNames); } @@ -58,10 +58,11 @@ namespace jassert (font != nullptr); ComSmartPtr faceNames; HRESULT hr = font->GetFaceNames (faceNames.resetAndGetPointerAddress()); - jassert (SUCCEEDED (hr)); (void) hr; - + jassert (SUCCEEDED (hr)); ignoreUnused (hr); return getLocalisedName (faceNames); } + + inline Point convertPoint (D2D1_POINT_2F p) noexcept { return Point ((float) p.x, (float) p.y); } } class Direct2DFactories @@ -97,6 +98,18 @@ public: if (directWriteFactory != nullptr) directWriteFactory->GetSystemFontCollection (systemFonts.resetAndGetPointerAddress()); } + + if (d2dFactory != nullptr) + { + D2D1_RENDER_TARGET_PROPERTIES d2dRTProp = D2D1::RenderTargetProperties (D2D1_RENDER_TARGET_TYPE_SOFTWARE, + D2D1::PixelFormat (DXGI_FORMAT_B8G8R8A8_UNORM, + D2D1_ALPHA_MODE_IGNORE), + 0, 0, + D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE, + D2D1_FEATURE_LEVEL_DEFAULT); + + d2dFactory->CreateDCRenderTarget (&d2dRTProp, directWriteRenderTarget.resetAndGetPointerAddress()); + } } } @@ -105,11 +118,13 @@ public: d2dFactory = nullptr; // (need to make sure these are released before deleting the DynamicLibrary objects) directWriteFactory = nullptr; systemFonts = nullptr; + directWriteRenderTarget = nullptr; } ComSmartPtr d2dFactory; ComSmartPtr directWriteFactory; ComSmartPtr systemFonts; + ComSmartPtr directWriteRenderTarget; private: DynamicLibrary direct2dDll, directWriteDll; @@ -197,10 +212,10 @@ public: const CharPointer_UTF32 textUTF32 (text.toUTF32()); const size_t len = textUTF32.length(); - HeapBlock glyphIndices (len); + HeapBlock glyphIndices (len); dwFontFace->GetGlyphIndices (textUTF32, (UINT32) len, glyphIndices); - HeapBlock dwGlyphMetrics (len); + HeapBlock dwGlyphMetrics (len); dwFontFace->GetDesignGlyphMetrics (glyphIndices, (UINT32) len, dwGlyphMetrics, false); float x = 0; @@ -210,16 +225,16 @@ public: return x * unitsToHeightScaleFactor; } - void getGlyphPositions (const String& text, Array & resultGlyphs, Array & xOffsets) + void getGlyphPositions (const String& text, Array& resultGlyphs, Array& xOffsets) { xOffsets.add (0); const CharPointer_UTF32 textUTF32 (text.toUTF32()); const size_t len = textUTF32.length(); - HeapBlock glyphIndices (len); + HeapBlock glyphIndices (len); dwFontFace->GetGlyphIndices (textUTF32, (UINT32) len, glyphIndices); - HeapBlock dwGlyphMetrics (len); + HeapBlock dwGlyphMetrics (len); dwFontFace->GetDesignGlyphMetrics (glyphIndices, (UINT32) len, dwGlyphMetrics, false); float x = 0; @@ -255,49 +270,45 @@ private: int designUnitsPerEm; AffineTransform pathTransform; - class PathGeometrySink : public ComBaseClassHelper + struct PathGeometrySink : public ComBaseClassHelper { - public: PathGeometrySink() : ComBaseClassHelper (0) {} - void __stdcall AddBeziers (const D2D1_BEZIER_SEGMENT *beziers, UINT beziersCount) + void __stdcall AddBeziers (const D2D1_BEZIER_SEGMENT* beziers, UINT beziersCount) override { for (UINT i = 0; i < beziersCount; ++i) - path.cubicTo ((float) beziers[i].point1.x, (float) beziers[i].point1.y, - (float) beziers[i].point2.x, (float) beziers[i].point2.y, - (float) beziers[i].point3.x, (float) beziers[i].point3.y); + path.cubicTo (convertPoint (beziers[i].point1), + convertPoint (beziers[i].point2), + convertPoint (beziers[i].point3)); } - void __stdcall AddLines (const D2D1_POINT_2F* points, UINT pointsCount) + void __stdcall AddLines (const D2D1_POINT_2F* points, UINT pointsCount) override { for (UINT i = 0; i < pointsCount; ++i) - path.lineTo ((float) points[i].x, - (float) points[i].y); + path.lineTo (convertPoint (points[i])); } - void __stdcall BeginFigure (D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN) + void __stdcall BeginFigure (D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN) override { - path.startNewSubPath ((float) startPoint.x, - (float) startPoint.y); + path.startNewSubPath (convertPoint (startPoint)); } - void __stdcall EndFigure (D2D1_FIGURE_END figureEnd) + void __stdcall EndFigure (D2D1_FIGURE_END figureEnd) override { if (figureEnd == D2D1_FIGURE_END_CLOSED) path.closeSubPath(); } - void __stdcall SetFillMode (D2D1_FILL_MODE fillMode) + void __stdcall SetFillMode (D2D1_FILL_MODE fillMode) override { path.setUsingNonZeroWinding (fillMode == D2D1_FILL_MODE_WINDING); } - void __stdcall SetSegmentFlags (D2D1_PATH_SEGMENT) {} - JUCE_COMRESULT Close() { return S_OK; } + void __stdcall SetSegmentFlags (D2D1_PATH_SEGMENT) override {} + JUCE_COMRESULT Close() override { return S_OK; } Path path; - private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PathGeometrySink) }; diff --git a/source/modules/juce_graphics/native/juce_win32_Fonts.cpp b/source/modules/juce_graphics/native/juce_win32_Fonts.cpp index db57fd198..9d84483df 100644 --- a/source/modules/juce_graphics/native/juce_win32_Fonts.cpp +++ b/source/modules/juce_graphics/native/juce_win32_Fonts.cpp @@ -363,7 +363,7 @@ public: results[numChars] = -1; float x = 0; - if (GetGlyphIndices (dc, utf16, (int) numChars, reinterpret_cast (results.getData()), + if (GetGlyphIndices (dc, utf16, (int) numChars, reinterpret_cast (results.getData()), GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR) { for (size_t i = 0; i < numChars; ++i) @@ -381,7 +381,7 @@ public: results[numChars] = -1; float x = 0; - if (GetGlyphIndices (dc, utf16, (int) numChars, reinterpret_cast (results.getData()), + if (GetGlyphIndices (dc, utf16, (int) numChars, reinterpret_cast (results.getData()), GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR) { resultGlyphs.ensureStorageAllocated ((int) numChars); diff --git a/source/modules/juce_graphics/placement/juce_RectanglePlacement.cpp b/source/modules/juce_graphics/placement/juce_RectanglePlacement.cpp index c13e505b2..1cfed08f6 100644 --- a/source/modules/juce_graphics/placement/juce_RectanglePlacement.cpp +++ b/source/modules/juce_graphics/placement/juce_RectanglePlacement.cpp @@ -89,7 +89,7 @@ void RectanglePlacement::applyTo (double& x, double& y, double& w, double& h, AffineTransform RectanglePlacement::getTransformToFit (const Rectangle& source, const Rectangle& destination) const noexcept { if (source.isEmpty()) - return AffineTransform::identity; + return AffineTransform(); float newX = destination.getX(); float newY = destination.getY(); diff --git a/source/modules/juce_graphics/placement/juce_RectanglePlacement.h b/source/modules/juce_graphics/placement/juce_RectanglePlacement.h index fd5b24e75..ec99bee93 100644 --- a/source/modules/juce_graphics/placement/juce_RectanglePlacement.h +++ b/source/modules/juce_graphics/placement/juce_RectanglePlacement.h @@ -151,10 +151,10 @@ public: const Rectangle& destination) const noexcept { double x = source.getX(), y = source.getY(), w = source.getWidth(), h = source.getHeight(); - applyTo (x, y, w, h, static_cast (destination.getX()), static_cast (destination.getY()), - static_cast (destination.getWidth()), static_cast (destination.getHeight())); - return Rectangle (static_cast (x), static_cast (y), - static_cast (w), static_cast (h)); + applyTo (x, y, w, h, static_cast (destination.getX()), static_cast (destination.getY()), + static_cast (destination.getWidth()), static_cast (destination.getHeight())); + return Rectangle (static_cast (x), static_cast (y), + static_cast (w), static_cast (h)); } /** Returns the transform that should be applied to these source coordinates to fit them diff --git a/source/modules/juce_gui_basics.h b/source/modules/juce_gui_basics.h deleted file mode 100644 index 17f61ca22..000000000 --- a/source/modules/juce_gui_basics.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Carla Juce setup - * Copyright (C) 2013-2014 Filipe Coelho - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or any later version. - * - * This program 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. - * - * For a full copy of the GNU General Public License see the doc/GPL.txt file. - */ - -#ifndef CARLA_JUCE_GUI_BASICS_H_INCLUDED -#define CARLA_JUCE_GUI_BASICS_H_INCLUDED - -#include "juce_graphics.h" -#include "juce_data_structures.h" - -#if JUCE_MAC || JUCE_WINDOWS -# include "juce_gui_basics/AppConfig.h" -# include "juce_gui_basics/juce_gui_basics.h" -#endif - -#endif // CARLA_JUCE_GUI_BASICS_H_INCLUDED diff --git a/source/modules/juce_gui_basics/AppConfig.h b/source/modules/juce_gui_basics/AppConfig.h deleted file mode 100755 index 8b881f9a0..000000000 --- a/source/modules/juce_gui_basics/AppConfig.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - ============================================================================== - - Build options for juce_gui_basics static library - - ============================================================================== -*/ - -#ifndef CARLA_JUCE_GUI_BASICS_APPCONFIG_H_INCLUDED -#define CARLA_JUCE_GUI_BASICS_APPCONFIG_H_INCLUDED - -#include "../juce_graphics/AppConfig.h" -#include "../juce_data_structures/AppConfig.h" - -//============================================================================= -/** Config: JUCE_ENABLE_REPAINT_DEBUGGING - If this option is turned on, each area of the screen that gets repainted will - flash in a random colour, so that you can see exactly which bits of your - components are being drawn. -*/ -#define JUCE_ENABLE_REPAINT_DEBUGGING 0 - -/** Config: JUCE_USE_XSHM - Enables X shared memory for faster rendering on Linux. This is best left turned on - unless you have a good reason to disable it. -*/ -#define JUCE_USE_XSHM 1 - -/** Config: JUCE_USE_XRENDER - Enables XRender to allow semi-transparent windowing on Linux. -*/ -#define JUCE_USE_XRENDER 0 - -/** Config: JUCE_USE_XCURSOR - Uses XCursor to allow ARGB cursor on Linux. This is best left turned on unless you have - a good reason to disable it. -*/ -#define JUCE_USE_XCURSOR 1 - -#endif // CARLA_JUCE_GUI_BASICS_APPCONFIG_H_INCLUDED diff --git a/source/modules/juce_gui_basics/Makefile b/source/modules/juce_gui_basics/Makefile index 31f1261e3..3af84b2b7 100644 --- a/source/modules/juce_gui_basics/Makefile +++ b/source/modules/juce_gui_basics/Makefile @@ -10,7 +10,11 @@ include ../Makefile.mk # ---------------------------------------------------------------------------------------------------------------------------- -BUILD_CXX_FLAGS += $(JUCE_GUI_BASICS_FLAGS) -w +BUILD_CXX_FLAGS += $(JUCE_GUI_BASICS_FLAGS) -I.. + +ifeq ($(WIN32),true) +BUILD_CXX_FLAGS += -Wno-missing-field-initializers -Wno-strict-aliasing -Wno-strict-overflow +endif # ---------------------------------------------------------------------------------------------------------------------------- diff --git a/source/modules/juce_gui_basics/application/juce_Application.cpp b/source/modules/juce_gui_basics/application/juce_Application.cpp index 47c4ee2a5..449f6c020 100644 --- a/source/modules/juce_gui_basics/application/juce_Application.cpp +++ b/source/modules/juce_gui_basics/application/juce_Application.cpp @@ -28,7 +28,7 @@ JUCEApplication::~JUCEApplication() {} //============================================================================== JUCEApplication* JUCE_CALLTYPE JUCEApplication::getInstance() noexcept { - return dynamic_cast (JUCEApplicationBase::getInstance()); + return dynamic_cast (JUCEApplicationBase::getInstance()); } bool JUCEApplication::moreThanOneInstanceAllowed() { return true; } diff --git a/source/modules/juce_gui_basics/buttons/juce_Button.cpp b/source/modules/juce_gui_basics/buttons/juce_Button.cpp index 3debe03d2..89181a348 100644 --- a/source/modules/juce_gui_basics/buttons/juce_Button.cpp +++ b/source/modules/juce_gui_basics/buttons/juce_Button.cpp @@ -244,7 +244,7 @@ void Button::turnOffOtherButtonsInGroup (const NotificationType notification) if (c != this) { - if (Button* const b = dynamic_cast (c)) + if (Button* const b = dynamic_cast (c)) { if (b->getRadioGroupId() == radioGroupId) { diff --git a/source/modules/juce_gui_basics/buttons/juce_ImageButton.cpp b/source/modules/juce_gui_basics/buttons/juce_ImageButton.cpp index 41a850422..dacad5426 100644 --- a/source/modules/juce_gui_basics/buttons/juce_ImageButton.cpp +++ b/source/modules/juce_gui_basics/buttons/juce_ImageButton.cpp @@ -178,6 +178,9 @@ void ImageButton::paintButton (Graphics& g, bool ImageButton::hitTest (int x, int y) { + if (! Component::hitTest (x, y)) // handle setInterceptsMouseClicks + return false; + if (alphaThreshold == 0) return true; diff --git a/source/modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.cpp b/source/modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.cpp index 734def2e6..092f6d2d8 100644 --- a/source/modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.cpp +++ b/source/modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.cpp @@ -78,7 +78,7 @@ bool ApplicationCommandTarget::tryToInvoke (const InvocationInfo& info, const bo ApplicationCommandTarget* ApplicationCommandTarget::findFirstTargetParentComponent() { - if (Component* const c = dynamic_cast (this)) + if (Component* const c = dynamic_cast (this)) return c->findParentComponentOfClass(); return nullptr; diff --git a/source/modules/juce_gui_basics/components/juce_Component.cpp b/source/modules/juce_gui_basics/components/juce_Component.cpp index 925da676f..100643442 100644 --- a/source/modules/juce_gui_basics/components/juce_Component.cpp +++ b/source/modules/juce_gui_basics/components/juce_Component.cpp @@ -1359,7 +1359,7 @@ bool Component::isTransformed() const noexcept AffineTransform Component::getTransform() const { - return affineTransform != nullptr ? *affineTransform : AffineTransform::identity; + return affineTransform != nullptr ? *affineTransform : AffineTransform(); } //============================================================================== diff --git a/source/modules/juce_gui_basics/components/juce_Component.h b/source/modules/juce_gui_basics/components/juce_Component.h index d4bbd58f5..7100365fd 100644 --- a/source/modules/juce_gui_basics/components/juce_Component.h +++ b/source/modules/juce_gui_basics/components/juce_Component.h @@ -432,7 +432,7 @@ public: /** Changes the size of the component. - A synchronous call to resized() will be occur if the size actually changes. + A synchronous call to resized() will occur if the size actually changes. Note that if you've used setTransform() to apply a transform, then the component's bounds will no longer be a direct reflection of the position at which it appears within @@ -574,7 +574,7 @@ public: Currently, transforms are not supported for desktop windows, so the transform will be ignored if you put a component on the desktop. - To remove a component's transform, simply pass AffineTransform::identity as the parameter to this method. + To remove a component's transform, simply pass AffineTransform() as the parameter to this method. */ void setTransform (const AffineTransform& transform); diff --git a/source/modules/juce_gui_basics/components/juce_ModalComponentManager.h b/source/modules/juce_gui_basics/components/juce_ModalComponentManager.h index df8b7b2de..b0817518a 100644 --- a/source/modules/juce_gui_basics/components/juce_ModalComponentManager.h +++ b/source/modules/juce_gui_basics/components/juce_ModalComponentManager.h @@ -328,7 +328,7 @@ private: void modalStateFinished (int returnValue) { - function (returnValue, static_cast (comp.get())); + function (returnValue, static_cast (comp.get())); } private: @@ -349,7 +349,7 @@ private: void modalStateFinished (int returnValue) { - function (returnValue, static_cast (comp.get()), param1); + function (returnValue, static_cast (comp.get()), param1); } private: diff --git a/source/modules/juce_gui_basics/drawables/juce_Drawable.cpp b/source/modules/juce_gui_basics/drawables/juce_Drawable.cpp index f9b0944d7..91d322e00 100644 --- a/source/modules/juce_gui_basics/drawables/juce_Drawable.cpp +++ b/source/modules/juce_gui_basics/drawables/juce_Drawable.cpp @@ -42,7 +42,7 @@ Drawable::~Drawable() //============================================================================== void Drawable::draw (Graphics& g, float opacity, const AffineTransform& transform) const { - const_cast (this)->nonConstDraw (g, opacity, transform); + const_cast (this)->nonConstDraw (g, opacity, transform); } void Drawable::nonConstDraw (Graphics& g, float opacity, const AffineTransform& transform) @@ -83,7 +83,7 @@ void Drawable::drawWithin (Graphics& g, const Rectangle& destArea, //============================================================================== DrawableComposite* Drawable::getParent() const { - return dynamic_cast (getParentComponent()); + return dynamic_cast (getParentComponent()); } void Drawable::transformContextToCorrectOrigin (Graphics& g) @@ -150,11 +150,11 @@ Drawable* Drawable::createFromImageData (const void* data, const size_t numBytes const String asString (String::createStringFromData (data, (int) numBytes)); XmlDocument doc (asString); - ScopedPointer outer (doc.getDocumentElement (true)); + ScopedPointer outer (doc.getDocumentElement (true)); if (outer != nullptr && outer->hasTagName ("svg")) { - ScopedPointer svg (doc.getDocumentElement()); + ScopedPointer svg (doc.getDocumentElement()); if (svg != nullptr) result = Drawable::createFromSVG (*svg); @@ -202,7 +202,7 @@ public: void updateComponentFromState (Component* component, const ValueTree& state) { - DrawableClass* const d = dynamic_cast (component); + DrawableClass* const d = dynamic_cast (component); jassert (d != nullptr); d->refreshFromValueTree (state, *this->getBuilder()); } @@ -224,7 +224,7 @@ Drawable* Drawable::createFromValueTree (const ValueTree& tree, ComponentBuilder registerDrawableTypeHandlers (builder); ScopedPointer comp (builder.createComponent()); - Drawable* const d = dynamic_cast (static_cast (comp)); + Drawable* const d = dynamic_cast (static_cast (comp)); if (d != nullptr) comp.release(); diff --git a/source/modules/juce_gui_basics/drawables/juce_Drawable.h b/source/modules/juce_gui_basics/drawables/juce_Drawable.h index f297d9c55..88e6936fe 100644 --- a/source/modules/juce_gui_basics/drawables/juce_Drawable.h +++ b/source/modules/juce_gui_basics/drawables/juce_Drawable.h @@ -63,7 +63,7 @@ public: @see drawWithin */ void draw (Graphics& g, float opacity, - const AffineTransform& transform = AffineTransform::identity) const; + const AffineTransform& transform = AffineTransform()) const; /** Renders the Drawable at a given offset within the Graphics context. diff --git a/source/modules/juce_gui_basics/drawables/juce_DrawableComposite.cpp b/source/modules/juce_gui_basics/drawables/juce_DrawableComposite.cpp index fa21d9233..0c0f6737d 100644 --- a/source/modules/juce_gui_basics/drawables/juce_DrawableComposite.cpp +++ b/source/modules/juce_gui_basics/drawables/juce_DrawableComposite.cpp @@ -40,7 +40,7 @@ DrawableComposite::DrawableComposite (const DrawableComposite& other) updateBoundsReentrant (false) { for (int i = 0; i < other.getNumChildComponents(); ++i) - if (const Drawable* const d = dynamic_cast (other.getChildComponent(i))) + if (const Drawable* const d = dynamic_cast (other.getChildComponent(i))) addAndMakeVisible (d->createCopy()); } @@ -60,7 +60,7 @@ Rectangle DrawableComposite::getDrawableBounds() const Rectangle r; for (int i = getNumChildComponents(); --i >= 0;) - if (const Drawable* const d = dynamic_cast (getChildComponent(i))) + if (const Drawable* const d = dynamic_cast (getChildComponent(i))) r = r.getUnion (d->isTransformed() ? d->getDrawableBounds().transformedBy (d->getTransform()) : d->getDrawableBounds()); @@ -148,7 +148,7 @@ void DrawableComposite::recalculateCoordinates (Expression::Scope* scope) content.getX(), content.getBottom(), resolved[2].x, resolved[2].y)); if (t.isSingularity()) - t = AffineTransform::identity; + t = AffineTransform(); setTransform (t); } @@ -314,7 +314,7 @@ ValueTree DrawableComposite::createValueTree (ComponentBuilder::ImageProvider* i for (int i = 0; i < getNumChildComponents(); ++i) { - const Drawable* const d = dynamic_cast (getChildComponent(i)); + const Drawable* const d = dynamic_cast (getChildComponent(i)); jassert (d != nullptr); // You can't save a mix of Drawables and normal components! childList.addChild (d->createValueTree (imageProvider), -1, nullptr); diff --git a/source/modules/juce_gui_basics/drawables/juce_DrawableImage.cpp b/source/modules/juce_gui_basics/drawables/juce_DrawableImage.cpp index 8eb51d26c..768364352 100644 --- a/source/modules/juce_gui_basics/drawables/juce_DrawableImage.cpp +++ b/source/modules/juce_gui_basics/drawables/juce_DrawableImage.cpp @@ -111,7 +111,7 @@ void DrawableImage::recalculateCoordinates (Expression::Scope* scope) bl.x, bl.y)); if (t.isSingularity()) - t = AffineTransform::identity; + t = AffineTransform(); setTransform (t); } diff --git a/source/modules/juce_gui_basics/drawables/juce_DrawableShape.cpp b/source/modules/juce_gui_basics/drawables/juce_DrawableShape.cpp index 16aad5ebb..b2a65d70b 100644 --- a/source/modules/juce_gui_basics/drawables/juce_DrawableShape.cpp +++ b/source/modules/juce_gui_basics/drawables/juce_DrawableShape.cpp @@ -178,7 +178,7 @@ void DrawableShape::pathChanged() void DrawableShape::strokeChanged() { strokePath.clear(); - strokeType.createStrokedPath (strokePath, path, AffineTransform::identity, 4.0f); + strokeType.createStrokedPath (strokePath, path, AffineTransform(), 4.0f); setBoundsToEnclose (getDrawableBounds()); repaint(); @@ -224,7 +224,7 @@ DrawableShape::RelativeFillType::RelativeFillType (const FillType& fill_) gradientPoint3 = Point (g.point1.x + g.point2.y - g.point1.y, g.point1.y + g.point1.x - g.point2.x) .transformedBy (fill.transform); - fill.transform = AffineTransform::identity; + fill.transform = AffineTransform(); } } @@ -375,7 +375,7 @@ bool DrawableShape::RelativeFillType::readFrom (const ValueTree& v, ComponentBui if (imageProvider != nullptr) im = imageProvider->getImageForIdentifier (v [FillAndStrokeState::imageId]); - fill.setTiledImage (im, AffineTransform::identity); + fill.setTiledImage (im, AffineTransform()); fill.setOpacity ((float) v.getProperty (FillAndStrokeState::imageOpacity, 1.0f)); return true; } diff --git a/source/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsDisplayComponent.cpp b/source/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsDisplayComponent.cpp index 287119357..552a676bf 100644 --- a/source/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsDisplayComponent.cpp +++ b/source/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsDisplayComponent.cpp @@ -48,7 +48,7 @@ void DirectoryContentsDisplayComponent::removeListener (FileBrowserListener* con void DirectoryContentsDisplayComponent::sendSelectionChangeMessage() { - Component::BailOutChecker checker (dynamic_cast (this)); + Component::BailOutChecker checker (dynamic_cast (this)); listeners.callChecked (checker, &FileBrowserListener::selectionChanged); } @@ -56,7 +56,7 @@ void DirectoryContentsDisplayComponent::sendMouseClickMessage (const File& file, { if (fileList.getDirectory().exists()) { - Component::BailOutChecker checker (dynamic_cast (this)); + Component::BailOutChecker checker (dynamic_cast (this)); listeners.callChecked (checker, &FileBrowserListener::fileClicked, file, e); } } @@ -65,7 +65,7 @@ void DirectoryContentsDisplayComponent::sendDoubleClickMessage (const File& file { if (fileList.getDirectory().exists()) { - Component::BailOutChecker checker (dynamic_cast (this)); + Component::BailOutChecker checker (dynamic_cast (this)); listeners.callChecked (checker, &FileBrowserListener::fileDoubleClicked, file); } } diff --git a/source/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp b/source/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp index 2adaa9a51..3239085fc 100644 --- a/source/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp +++ b/source/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp @@ -153,7 +153,7 @@ void FileSearchPathListComponent::returnKeyPressed (int row) changed(); } #else - (void) row; + ignoreUnused (row); #endif } diff --git a/source/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.cpp b/source/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.cpp index 51f20f3ae..178a10b3d 100644 --- a/source/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.cpp +++ b/source/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.cpp @@ -133,7 +133,7 @@ public: for (int maxRetries = 500; --maxRetries > 0;) { for (int i = 0; i < getNumSubItems(); ++i) - if (FileListTreeItem* f = dynamic_cast (getSubItem (i))) + if (FileListTreeItem* f = dynamic_cast (getSubItem (i))) if (f->selectFile (target)) return true; @@ -282,7 +282,7 @@ void FileTreeComponent::refresh() //============================================================================== File FileTreeComponent::getSelectedFile (const int index) const { - if (const FileListTreeItem* const item = dynamic_cast (getSelectedItem (index))) + if (const FileListTreeItem* const item = dynamic_cast (getSelectedItem (index))) return item->file; return File::nonexistent; @@ -305,7 +305,7 @@ void FileTreeComponent::setDragAndDropDescription (const String& description) void FileTreeComponent::setSelectedFile (const File& target) { - if (FileListTreeItem* t = dynamic_cast (getRootItem())) + if (FileListTreeItem* t = dynamic_cast (getRootItem())) if (! t->selectFile (target)) clearSelectedItems(); } diff --git a/source/modules/juce_gui_basics/juce_gui_basics.cpp b/source/modules/juce_gui_basics/juce_gui_basics.cpp index 8f240494e..33a595543 100644 --- a/source/modules/juce_gui_basics/juce_gui_basics.cpp +++ b/source/modules/juce_gui_basics/juce_gui_basics.cpp @@ -22,7 +22,7 @@ ============================================================================== */ -#if defined (JUCE_GUI_BASICS_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE +#ifdef JUCE_GUI_BASICS_H_INCLUDED /* 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 @@ -31,19 +31,11 @@ #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" - #define NS_FORMAT_FUNCTION(F,A) // To avoid spurious warnings from GCC #include "../juce_core/native/juce_BasicNativeHeaders.h" #include "juce_gui_basics.h" -#if JUCE_MODULE_AVAILABLE_juce_opengl - #include "../juce_opengl/juce_opengl.h" -#endif - //============================================================================== #if JUCE_MAC #import diff --git a/source/modules/juce_gui_basics/layout/juce_ComponentAnimator.h b/source/modules/juce_gui_basics/layout/juce_ComponentAnimator.h index 966863e86..0b573e1e5 100644 --- a/source/modules/juce_gui_basics/layout/juce_ComponentAnimator.h +++ b/source/modules/juce_gui_basics/layout/juce_ComponentAnimator.h @@ -152,7 +152,7 @@ private: uint32 lastTime; AnimationTask* findTaskFor (Component*) const noexcept; - void timerCallback(); + void timerCallback() override; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentAnimator) }; diff --git a/source/modules/juce_gui_basics/layout/juce_ComponentBuilder.cpp b/source/modules/juce_gui_basics/layout/juce_ComponentBuilder.cpp index 4c5c5653a..da2e247b1 100644 --- a/source/modules/juce_gui_basics/layout/juce_ComponentBuilder.cpp +++ b/source/modules/juce_gui_basics/layout/juce_ComponentBuilder.cpp @@ -109,7 +109,7 @@ ComponentBuilder::~ComponentBuilder() #if JUCE_DEBUG // Don't delete the managed component!! The builder owns that component, and will delete // it automatically when it gets deleted. - jassert (componentRef.get() == static_cast (component)); + jassert (componentRef.get() == static_cast (component)); #endif } diff --git a/source/modules/juce_gui_basics/layout/juce_MultiDocumentPanel.cpp b/source/modules/juce_gui_basics/layout/juce_MultiDocumentPanel.cpp index 60ba1572f..69c40bb9f 100644 --- a/source/modules/juce_gui_basics/layout/juce_MultiDocumentPanel.cpp +++ b/source/modules/juce_gui_basics/layout/juce_MultiDocumentPanel.cpp @@ -137,7 +137,7 @@ void MultiDocumentPanel::addWindow (Component* component) dw->setName (component->getName()); const var bkg (component->getProperties() ["mdiDocumentBkg_"]); - dw->setBackgroundColour (bkg.isVoid() ? backgroundColour : Colour ((uint32) static_cast (bkg))); + dw->setBackgroundColour (bkg.isVoid() ? backgroundColour : Colour ((uint32) static_cast (bkg))); int x = 4; @@ -161,7 +161,7 @@ bool MultiDocumentPanel::addDocument (Component* const component, { // If you try passing a full DocumentWindow or ResizableWindow in here, you'll end up // with a frame-within-a-frame! Just pass in the bare content component. - jassert (dynamic_cast (component) == nullptr); + jassert (dynamic_cast (component) == nullptr); if (component == nullptr || (maximumNumDocuments > 0 && components.size() >= maximumNumDocuments)) return false; @@ -239,7 +239,7 @@ bool MultiDocumentPanel::closeDocument (Component* component, { for (int i = getNumChildComponents(); --i >= 0;) { - if (MultiDocumentPanelWindow* const dw = dynamic_cast (getChildComponent (i))) + if (MultiDocumentPanelWindow* const dw = dynamic_cast (getChildComponent (i))) { if (dw->getContentComponent() == component) { @@ -258,7 +258,7 @@ bool MultiDocumentPanel::closeDocument (Component* component, { for (int i = getNumChildComponents(); --i >= 0;) { - ScopedPointer dw (dynamic_cast (getChildComponent (i))); + ScopedPointer dw (dynamic_cast (getChildComponent (i))); if (dw != nullptr) dw->clearContentComponent(); @@ -325,7 +325,7 @@ Component* MultiDocumentPanel::getActiveDocument() const noexcept if (mode == FloatingWindows) { for (int i = getNumChildComponents(); --i >= 0;) - if (MultiDocumentPanelWindow* const dw = dynamic_cast (getChildComponent (i))) + if (MultiDocumentPanelWindow* const dw = dynamic_cast (getChildComponent (i))) if (dw->isActiveWindow()) return dw->getContentComponent(); } @@ -397,7 +397,7 @@ void MultiDocumentPanel::setLayoutMode (const LayoutMode newLayoutMode) { for (int i = getNumChildComponents(); --i >= 0;) { - ScopedPointer dw (dynamic_cast (getChildComponent (i))); + ScopedPointer dw (dynamic_cast (getChildComponent (i))); if (dw != nullptr) { @@ -417,7 +417,7 @@ void MultiDocumentPanel::setLayoutMode (const LayoutMode newLayoutMode) Component* const c = tempComps.getUnchecked(i); addDocument (c, - Colour ((uint32) static_cast (c->getProperties().getWithDefault ("mdiDocumentBkg_", (int) Colours::white.getARGB()))), + Colour ((uint32) static_cast (c->getProperties().getWithDefault ("mdiDocumentBkg_", (int) Colours::white.getARGB()))), MultiDocHelpers::shouldDeleteComp (c)); } } @@ -455,7 +455,7 @@ Component* MultiDocumentPanel::getContainerComp (Component* c) const if (mode == FloatingWindows) { for (int i = 0; i < getNumChildComponents(); ++i) - if (MultiDocumentPanelWindow* const dw = dynamic_cast (getChildComponent (i))) + if (MultiDocumentPanelWindow* const dw = dynamic_cast (getChildComponent (i))) if (dw->getContentComponent() == c) return dw; } @@ -468,7 +468,7 @@ void MultiDocumentPanel::componentNameChanged (Component&) if (mode == FloatingWindows) { for (int i = 0; i < getNumChildComponents(); ++i) - if (MultiDocumentPanelWindow* const dw = dynamic_cast (getChildComponent (i))) + if (MultiDocumentPanelWindow* const dw = dynamic_cast (getChildComponent (i))) dw->setName (dw->getContentComponent()->getName()); } else if (tabComponent != nullptr) @@ -487,7 +487,7 @@ void MultiDocumentPanel::updateOrder() components.clear(); for (int i = 0; i < getNumChildComponents(); ++i) - if (MultiDocumentPanelWindow* const dw = dynamic_cast (getChildComponent (i))) + if (MultiDocumentPanelWindow* const dw = dynamic_cast (getChildComponent (i))) components.add (dw->getContentComponent()); } else diff --git a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp index 13c821f29..af92bd810 100644 --- a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp +++ b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp @@ -1605,7 +1605,7 @@ void LookAndFeel_V2::layoutFilenameComponent (FilenameComponent& filenameComp, { browseButton->setSize (80, filenameComp.getHeight()); - if (TextButton* const tb = dynamic_cast (browseButton)) + if (TextButton* const tb = dynamic_cast (browseButton)) tb->changeWidthToFitText(); browseButton->setTopRightPosition (filenameComp.getWidth(), 0); @@ -2575,7 +2575,7 @@ void LookAndFeel_V2::layoutFileBrowserComponent (FileBrowserComponent& browserCo y += controlsHeight + 4; - if (Component* const listAsComp = dynamic_cast (fileListComponent)) + if (Component* const listAsComp = dynamic_cast (fileListComponent)) { listAsComp->setBounds (x, y, w, browserComp.getHeight() - y - bottomSectionHeight); y = listAsComp->getBottom() + 4; diff --git a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.cpp b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.cpp index 3e8e135c3..72f093325 100644 --- a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.cpp +++ b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.cpp @@ -457,7 +457,7 @@ void LookAndFeel_V3::drawLinearSliderBackground (Graphics& g, int x, int y, int void LookAndFeel_V3::drawPopupMenuBackground (Graphics& g, int width, int height) { g.fillAll (findColour (PopupMenu::backgroundColourId)); - (void) width; (void) height; + ignoreUnused (width, height); #if ! JUCE_MAC g.setColour (findColour (PopupMenu::textColourId).withAlpha (0.6f)); diff --git a/source/modules/juce_gui_basics/menus/juce_PopupMenu.cpp b/source/modules/juce_gui_basics/menus/juce_PopupMenu.cpp index 4595b7f13..795410488 100644 --- a/source/modules/juce_gui_basics/menus/juce_PopupMenu.cpp +++ b/source/modules/juce_gui_basics/menus/juce_PopupMenu.cpp @@ -107,8 +107,8 @@ public: const Colour textColour; const bool isActive, isSeparator, isTicked, usesColour; ScopedPointer iconDrawable; - ReferenceCountedObjectPtr customComp; - ScopedPointer subMenu; + ReferenceCountedObjectPtr customComp; + ScopedPointer subMenu; ApplicationCommandManager* const commandManager; private: @@ -1544,7 +1544,7 @@ int PopupMenu::showWithOptionalCallback (const Options& options, ModalComponentM if (userCallback == nullptr && canBeModal) return window->runModalLoop(); #else - (void) canBeModal; + ignoreUnused (canBeModal); jassert (! (userCallback == nullptr && canBeModal)); #endif } diff --git a/source/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.h b/source/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.h index 7a1cd17e9..0b050237a 100644 --- a/source/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.h +++ b/source/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.h @@ -181,7 +181,7 @@ private: friend struct ContainerDeletePolicy; ScopedPointer dragImageComponent; - JUCE_DEPRECATED (virtual bool shouldDropFilesWhenDraggedExternally (const String&, Component*, StringArray&, bool&)) { return false; } + JUCE_DEPRECATED_WITH_BODY (virtual bool shouldDropFilesWhenDraggedExternally (const String&, Component*, StringArray&, bool&), { return false; }) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DragAndDropContainer) }; diff --git a/source/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm b/source/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm index f6de8d650..6c1f7efd1 100644 --- a/source/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm +++ b/source/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm @@ -317,15 +317,14 @@ static void sendScreenBoundsUpdate (JuceUIViewController* c) - (void) willRotateToInterfaceOrientation: (UIInterfaceOrientation) toInterfaceOrientation duration: (NSTimeInterval) duration { - (void) toInterfaceOrientation; - (void) duration; + ignoreUnused (toInterfaceOrientation, duration); [UIView setAnimationsEnabled: NO]; // disable this because it goes the wrong way and looks like crap. } - (void) didRotateFromInterfaceOrientation: (UIInterfaceOrientation) fromInterfaceOrientation { - (void) fromInterfaceOrientation; + ignoreUnused (fromInterfaceOrientation); sendScreenBoundsUpdate (self); [UIView setAnimationsEnabled: YES]; } @@ -347,13 +346,13 @@ static void sendScreenBoundsUpdate (JuceUIViewController* c) - (void) viewWillAppear: (BOOL) animated { - (void) animated; + ignoreUnused (animated); [self viewDidLoad]; } - (void) viewDidAppear: (BOOL) animated { - (void) animated; + ignoreUnused (animated); [self viewDidLoad]; } @@ -405,7 +404,7 @@ static void sendScreenBoundsUpdate (JuceUIViewController* c) //============================================================================== - (void) touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event { - (void) touches; + ignoreUnused (touches); if (owner != nullptr) owner->handleTouches (event, true, false, false); @@ -413,7 +412,7 @@ static void sendScreenBoundsUpdate (JuceUIViewController* c) - (void) touchesMoved: (NSSet*) touches withEvent: (UIEvent*) event { - (void) touches; + ignoreUnused (touches); if (owner != nullptr) owner->handleTouches (event, false, false, false); @@ -421,7 +420,7 @@ static void sendScreenBoundsUpdate (JuceUIViewController* c) - (void) touchesEnded: (NSSet*) touches withEvent: (UIEvent*) event { - (void) touches; + ignoreUnused (touches); if (owner != nullptr) owner->handleTouches (event, false, true, false); @@ -459,7 +458,7 @@ static void sendScreenBoundsUpdate (JuceUIViewController* c) - (BOOL) textView: (UITextView*) textView shouldChangeTextInRange: (NSRange) range replacementText: (NSString*) text { - (void) textView; + ignoreUnused (textView); return owner->textViewReplaceCharacters (Range ((int) range.location, (int) (range.location + range.length)), nsStringToJuce (text)); } @@ -759,6 +758,26 @@ void UIViewComponentPeer::setIcon (const Image& /*newIcon*/) } //============================================================================== +static float getMaximumTouchForce (UITouch* touch) noexcept +{ + #if defined (__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0 + if ([touch respondsToSelector: @selector (maximumPossibleForce)]) + return (float) touch.maximumPossibleForce; + #endif + + return 0.0f; +} + +static float getTouchForce (UITouch* touch) noexcept +{ + #if defined (__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0 + if ([touch respondsToSelector: @selector (force)]) + return (float) touch.force; + #endif + + return 0.0f; +} + void UIViewComponentPeer::handleTouches (UIEvent* event, const bool isDown, const bool isUp, bool isCancel) { NSArray* touches = [[event touchesForView: view] allObjects]; @@ -766,12 +785,9 @@ void UIViewComponentPeer::handleTouches (UIEvent* event, const bool isDown, cons for (unsigned int i = 0; i < [touches count]; ++i) { UITouch* touch = [touches objectAtIndex: i]; + const float maximumForce = getMaximumTouchForce (touch); - #if defined (__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0 - if ([touch phase] == UITouchPhaseStationary && touch.maximumPossibleForce <= 0) - #else - if ([touch phase] == UITouchPhaseStationary) - #endif + if ([touch phase] == UITouchPhaseStationary && maximumForce <= 0) continue; CGPoint p = [touch locationInView: view]; @@ -816,13 +832,9 @@ void UIViewComponentPeer::handleTouches (UIEvent* event, const bool isDown, cons modsToSend = currentModifiers = currentModifiers.withoutMouseButtons(); } - float pressure = MouseInputSource::invalidPressure; - - #if defined (__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0 - if (touch.maximumPossibleForce > 0) - // NB: other devices return 0 or 1.0 if pressure is unknown, so we'll clip our value to a believable range: - pressure = jlimit (0.0001f, 0.9999f, (float) (touch.force / touch.maximumPossibleForce)); - #endif + // NB: some devices return 0 or 1.0 if pressure is unknown, so we'll clip our value to a believable range: + float pressure = maximumForce > 0 ? jlimit (0.0001f, 0.9999f, getTouchForce (touch) / maximumForce) + : MouseInputSource::invalidPressure; handleMouseEvent (touchIndex, pos, modsToSend, pressure, time); diff --git a/source/modules/juce_gui_basics/native/juce_ios_Windowing.mm b/source/modules/juce_gui_basics/native/juce_ios_Windowing.mm index 4d69dd53e..10a9607f9 100644 --- a/source/modules/juce_gui_basics/native/juce_ios_Windowing.mm +++ b/source/modules/juce_gui_basics/native/juce_ios_Windowing.mm @@ -357,7 +357,7 @@ void Desktop::Displays::findDisplays (float masterScale) UIScreen* s = [UIScreen mainScreen]; Display d; - d.userArea = UIViewComponentPeer::realScreenPosToRotated (convertToRectInt ([s applicationFrame])) / masterScale; + d.userArea = UIViewComponentPeer::realScreenPosToRotated (convertToRectInt ([s bounds])) / masterScale; d.totalArea = UIViewComponentPeer::realScreenPosToRotated (convertToRectInt ([s bounds])) / masterScale; d.isMain = true; d.scale = masterScale; diff --git a/source/modules/juce_gui_basics/native/juce_linux_Clipboard.cpp b/source/modules/juce_gui_basics/native/juce_linux_Clipboard.cpp index a6291877e..0427e9458 100644 --- a/source/modules/juce_gui_basics/native/juce_linux_Clipboard.cpp +++ b/source/modules/juce_gui_basics/native/juce_linux_Clipboard.cpp @@ -142,7 +142,7 @@ namespace ClipboardHelpers reply.property = None; // == "fail" reply.time = evt.time; - HeapBlock data; + HeapBlock data; int propertyFormat = 0; size_t numDataItems = 0; diff --git a/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp b/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp index 79f81e929..7ac69a0ab 100644 --- a/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp +++ b/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp @@ -76,12 +76,6 @@ struct Atoms allowedActions[4] = XdndActionPrivate; } - static const Atoms& get() - { - static Atoms atoms; - return atoms; - } - enum ProtocolItems { TAKE_FOCUS = 0, @@ -104,7 +98,7 @@ struct Atoms static Atom getIfExists (const char* name) { return XInternAtom (display, name, True); } static Atom getCreating (const char* name) { return XInternAtom (display, name, False); } - static String getName (const Atom atom) + static String getName (const Atom& atom) { if (atom == None) return "None"; @@ -112,7 +106,7 @@ struct Atoms return String (XGetAtomName (display, atom)); } - static bool isMimeTypeFile (const Atom atom) { return getName (atom).equalsIgnoreCase ("text/uri-list"); } + static bool isMimeTypeFile (const Atom& atom) { return getName (atom).equalsIgnoreCase ("text/uri-list"); } }; const unsigned long Atoms::DndVersion = 3; @@ -328,7 +322,8 @@ namespace XRender static bool hasCompositingWindowManager() { - return display != nullptr && XGetSelectionOwner (display, Atoms::get().compositingManager) != 0; + const Atom compositingManager = Atoms::getCreating ("_NET_WM_CM_S0"); + return display != nullptr && XGetSelectionOwner (display, compositingManager) != 0; } static XRenderPictFormat* findPictureFormat() @@ -1611,7 +1606,7 @@ public: clientMsg.window = windowH; clientMsg.type = ClientMessage; clientMsg.format = 32; - clientMsg.message_type = Atoms::get().windowState; + clientMsg.message_type = atoms.windowState; clientMsg.data.l[0] = 0; // Remove clientMsg.data.l[1] = (long) fs; clientMsg.data.l[2] = 0; @@ -1703,7 +1698,7 @@ public: clientMsg.window = windowH; clientMsg.type = ClientMessage; clientMsg.format = 32; - clientMsg.message_type = Atoms::get().changeState; + clientMsg.message_type = atoms.changeState; clientMsg.data.l[0] = IconicState; ScopedXLock xlock; @@ -1718,7 +1713,6 @@ public: bool isMinimised() const override { ScopedXLock xlock; - const Atoms& atoms = Atoms::get(); GetXProperty prop (windowH, atoms.state, 0, 64, false, atoms.state); return prop.success @@ -1853,7 +1847,7 @@ public: ev.xclient.type = ClientMessage; ev.xclient.serial = 0; ev.xclient.send_event = True; - ev.xclient.message_type = Atoms::get().activeWin; + ev.xclient.message_type = atoms.activeWin; ev.xclient.window = windowH; ev.xclient.format = 32; ev.xclient.data.l[0] = 2; @@ -2412,8 +2406,6 @@ public: void handleClientMessageEvent (XClientMessageEvent& clientMsg, XEvent& event) { - const Atoms& atoms = Atoms::get(); - if (clientMsg.message_type == atoms.protocols && clientMsg.format == 32) { const Atom atom = (Atom) clientMsg.data.l[0]; @@ -2684,6 +2676,7 @@ private: JUCE_DECLARE_NON_COPYABLE (LinuxRepaintManager) }; + const Atoms atoms; ScopedPointer repainter; friend class LinuxRepaintManager; @@ -2918,7 +2911,7 @@ private: netHints[1] = Atoms::getIfExists ("_KDE_NET_WM_WINDOW_TYPE_OVERRIDE"); - xchangeProperty (windowH, Atoms::get().windowType, XA_ATOM, 32, &netHints, 2); + xchangeProperty (windowH, atoms.windowType, XA_ATOM, 32, &netHints, 2); int numHints = 0; @@ -2929,7 +2922,7 @@ private: netHints [numHints++] = Atoms::getIfExists ("_NET_WM_STATE_ABOVE"); if (numHints > 0) - xchangeProperty (windowH, Atoms::get().windowState, XA_ATOM, 32, &netHints, numHints); + xchangeProperty (windowH, atoms.windowState, XA_ATOM, 32, &netHints, numHints); } void createWindow (Window parentToAddTo) @@ -3008,8 +3001,6 @@ private: setTitle (component.getName()); - const Atoms& atoms = Atoms::get(); - // Associate the PID, allowing to be shut down when something goes wrong unsigned long pid = (unsigned long) getpid(); xchangeProperty (windowH, atoms.pid, XA_CARDINAL, 32, &pid, 1); @@ -3073,7 +3064,7 @@ private: long getUserTime() const { - GetXProperty prop (windowH, Atoms::get().userTime, 0, 65536, false, XA_CARDINAL); + GetXProperty prop (windowH, atoms.userTime, 0, 65536, false, XA_CARDINAL); return prop.success ? *(long*) prop.data : 0; } @@ -3145,16 +3136,22 @@ private: Rectangle silentRect; String textOrFiles; - const Atom* getMimeTypes() const noexcept { return isText ? Atoms::get().externalAllowedTextMimeTypes - : Atoms::get().externalAllowedFileMimeTypes; } + const Atom* getMimeTypes (const Atoms& atoms) const noexcept + { + return isText ? atoms.externalAllowedTextMimeTypes + : atoms.externalAllowedFileMimeTypes; + } - int getNumMimeTypes() const noexcept { return isText ? numElementsInArray (Atoms::get().externalAllowedTextMimeTypes) - : numElementsInArray (Atoms::get().externalAllowedFileMimeTypes); } + int getNumMimeTypes (const Atoms& atoms) const noexcept + { + return isText ? numElementsInArray (atoms.externalAllowedTextMimeTypes) + : numElementsInArray (atoms.externalAllowedFileMimeTypes); + } - bool matchesTarget (Atom targetType) const + bool matchesTarget (const Atoms& atoms, Atom targetType) const { - for (int i = getNumMimeTypes(); --i >= 0;) - if (getMimeTypes()[i] == targetType) + for (int i = getNumMimeTypes(atoms); --i >= 0;) + if (getMimeTypes(atoms)[i] == targetType) return true; return false; @@ -3206,7 +3203,7 @@ private: XClientMessageEvent msg; zerostruct (msg); - msg.message_type = Atoms::get().XdndDrop; + msg.message_type = atoms.XdndDrop; msg.data.l[2] = CurrentTime; sendExternalDragAndDropMessage (msg, targetWindow); @@ -3217,10 +3214,10 @@ private: XClientMessageEvent msg; zerostruct (msg); - msg.message_type = Atoms::get().XdndEnter; + msg.message_type = atoms.XdndEnter; - const Atom* mimeTypes = dragState.getMimeTypes(); - const int numMimeTypes = dragState.getNumMimeTypes(); + const Atom* mimeTypes = dragState.getMimeTypes (atoms); + const int numMimeTypes = dragState.getNumMimeTypes (atoms); msg.data.l[1] = (dragState.xdndVersion << 24) | (numMimeTypes > 3); msg.data.l[2] = numMimeTypes > 0 ? (long) mimeTypes[0] : 0; @@ -3235,7 +3232,7 @@ private: XClientMessageEvent msg; zerostruct (msg); - msg.message_type = Atoms::get().XdndPosition; + msg.message_type = atoms.XdndPosition; Point mousePos (Desktop::getInstance().getMousePosition()); @@ -3246,7 +3243,7 @@ private: msg.data.l[1] = 0; msg.data.l[2] = (mousePos.x << 16) | mousePos.y; msg.data.l[3] = CurrentTime; - msg.data.l[4] = (long) Atoms::get().XdndActionCopy; // this is all JUCE currently supports + msg.data.l[4] = (long) atoms.XdndActionCopy; // this is all JUCE currently supports dragState.expectingStatus = sendExternalDragAndDropMessage (msg, targetWindow); } @@ -3256,7 +3253,7 @@ private: XClientMessageEvent msg; zerostruct (msg); - msg.message_type = Atoms::get().XdndStatus; + msg.message_type = atoms.XdndStatus; msg.data.l[1] = (acceptDrop ? 1 : 0) | 2; // 2 indicates that we want to receive position messages msg.data.l[4] = (long) dropAction; @@ -3268,7 +3265,7 @@ private: XClientMessageEvent msg; zerostruct (msg); - msg.message_type = Atoms::get().XdndLeave; + msg.message_type = atoms.XdndLeave; sendExternalDragAndDropMessage (msg, targetWindow); } @@ -3277,7 +3274,7 @@ private: XClientMessageEvent msg; zerostruct (msg); - msg.message_type = Atoms::get().XdndFinished; + msg.message_type = atoms.XdndFinished; sendDragAndDropMessage (msg); } @@ -3299,7 +3296,7 @@ private: s.xselection.property = None; s.xselection.time = evt.xselectionrequest.time; - if (dragState.matchesTarget (targetType)) + if (dragState.matchesTarget (atoms, targetType)) { s.xselection.property = evt.xselectionrequest.property; @@ -3322,8 +3319,8 @@ private: dragState.silentRect = Rectangle(); if ((clientMsg.data.l[1] & 1) != 0 - && ((Atom) clientMsg.data.l[4] == Atoms::get().XdndActionCopy - || (Atom) clientMsg.data.l[4] == Atoms::get().XdndActionPrivate)) + && ((Atom) clientMsg.data.l[4] == atoms.XdndActionCopy + || (Atom) clientMsg.data.l[4] == atoms.XdndActionPrivate)) { if ((clientMsg.data.l[1] & 2) == 0) // target requests silent rectangle dragState.silentRect.setBounds ((int) clientMsg.data.l[2] >> 16, @@ -3367,7 +3364,7 @@ private: if (targetWindow == None) return; - GetXProperty prop (targetWindow, Atoms::get().XdndAware, + GetXProperty prop (targetWindow, atoms.XdndAware, 0, 2, false, AnyPropertyType); if (prop.success @@ -3402,7 +3399,6 @@ private: (int) clientMsg.data.l[2] & 0xffff); dropPos -= bounds.getPosition(); - const Atoms& atoms = Atoms::get(); Atom targetAction = atoms.XdndActionCopy; for (int i = numElementsInArray (atoms.allowedActions); --i >= 0;) @@ -3472,7 +3468,7 @@ private: if ((clientMsg.data.l[1] & 1) != 0) { ScopedXLock xlock; - GetXProperty prop (dragAndDropSourceWindow, Atoms::get().XdndTypeList, 0, 0x8000000L, false, XA_ATOM); + GetXProperty prop (dragAndDropSourceWindow, atoms.XdndTypeList, 0, 0x8000000L, false, XA_ATOM); if (prop.success && prop.actualType == XA_ATOM @@ -3500,7 +3496,6 @@ private: } } - const Atoms& atoms = Atoms::get(); for (int i = 0; i < srcMimeTypeAtomList.size() && dragAndDropCurrentMimeType == 0; ++i) for (int j = 0; j < numElementsInArray (atoms.allowedMimeTypes); ++j) if (srcMimeTypeAtomList[i] == atoms.allowedMimeTypes[j]) @@ -3564,7 +3559,7 @@ private: { ScopedXLock xlock; XConvertSelection (display, - Atoms::get().XdndSelection, + atoms.XdndSelection, dragAndDropCurrentMimeType, Atoms::getCreating ("JXSelectionWindowProperty"), windowH, @@ -3572,18 +3567,18 @@ private: } } - static bool isWindowDnDAware (Window w) + bool isWindowDnDAware (Window w) const { int numProperties = 0; - Atom* const atoms = XListProperties (display, w, &numProperties); + Atom* const watoms = XListProperties (display, w, &numProperties); bool dndAwarePropFound = false; for (int i = 0; i < numProperties; ++i) - if (atoms[i] == Atoms::get().XdndAware) + if (watoms[i] == atoms.XdndAware) dndAwarePropFound = true; - if (atoms != nullptr) - XFree (atoms); + if (watoms != nullptr) + XFree (watoms); return dndAwarePropFound; } @@ -3623,13 +3618,12 @@ private: // No other method of changing the pointer seems to work, this call is needed from this very context XChangeActivePointerGrab (display, pointerGrabMask, (Cursor) createDraggingHandCursor(), CurrentTime); - const Atoms& atoms = Atoms::get(); XSetSelectionOwner (display, atoms.XdndSelection, windowH, CurrentTime); // save the available types to XdndTypeList xchangeProperty (windowH, atoms.XdndTypeList, XA_ATOM, 32, - dragState.getMimeTypes(), - dragState.getNumMimeTypes()); + dragState.getMimeTypes (atoms), + dragState.getNumMimeTypes (atoms)); dragState.dragging = true; handleExternalDragMotionNotify(); diff --git a/source/modules/juce_gui_basics/native/juce_mac_MainMenu.mm b/source/modules/juce_gui_basics/native/juce_mac_MainMenu.mm index 99998e4f6..8ca3b614f 100644 --- a/source/modules/juce_gui_basics/native/juce_mac_MainMenu.mm +++ b/source/modules/juce_gui_basics/native/juce_mac_MainMenu.mm @@ -87,28 +87,6 @@ public: void updateTopLevelMenu (NSMenuItem* parentItem, const PopupMenu& menuToCopy, const String& name, const int menuId, const int tag) { - #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 - static bool is10_4 = (SystemStats::getOperatingSystemType() == SystemStats::MacOSX_10_4); - - if (is10_4) - { - [parentItem setTag: tag]; - NSMenu* menu = [parentItem submenu]; - - [menu setTitle: juceStringToNS (name)]; - - while ([menu numberOfItems] > 0) - [menu removeItemAtIndex: 0]; - - for (PopupMenu::MenuItemIterator iter (menuToCopy); iter.next();) - addMenuItem (iter, menu, menuId, tag); - - [menu setAutoenablesItems: false]; - [menu update]; - return; - } - #endif - // Note: This method used to update the contents of the existing menu in-place, but that caused // weird side-effects which messed-up keyboard focus when switching between windows. By creating // a new menu and replacing the old one with it, that problem seems to be avoided.. @@ -513,7 +491,7 @@ private: { if (juce::Component* focused = juce::Component::getCurrentlyFocusedComponent()) { - if (juce::NSViewComponentPeer* peer = dynamic_cast (focused->getPeer())) + if (juce::NSViewComponentPeer* peer = dynamic_cast (focused->getPeer())) { if ([e type] == NSKeyDown) peer->redirectKeyDown (e); diff --git a/source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm b/source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm index 096bb8f0f..9287ae669 100644 --- a/source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm +++ b/source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm @@ -245,24 +245,26 @@ public: fullScreen = isNowFullScreen; NSRect r = makeNSRect (newBounds); + NSSize oldViewSize = [view frame].size; if (isSharedWindow) { r.origin.y = [[view superview] frame].size.height - (r.origin.y + r.size.height); - - if ([view frame].size.width != r.size.width - || [view frame].size.height != r.size.height) - { - [view setNeedsDisplay: true]; - } - [view setFrame: r]; } else { + // Repaint behaviour of setFrame seemed to change in 10.11, and the drawing became synchronous, + // causing performance issues. But sending an async update causes flickering in older versions, + // hence this version check to use the old behaviour on pre 10.11 machines + static bool isPre10_11 = SystemStats::getOperatingSystemType() <= SystemStats::MacOSX_10_10; + [window setFrame: [window frameRectForContentRect: flippedScreenRect (r)] - display: true]; + display: isPre10_11]; } + + if (oldViewSize.width != r.size.width || oldViewSize.height != r.size.height) + [view setNeedsDisplay: true]; } Rectangle getBounds (const bool global) const @@ -307,27 +309,10 @@ public: void setAlpha (float newAlpha) override { - if (! isSharedWindow) - { - [window setAlphaValue: (CGFloat) newAlpha]; - } - else - { - #if defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 + if (isSharedWindow) [view setAlphaValue: (CGFloat) newAlpha]; - #else - if ([view respondsToSelector: @selector (setAlphaValue:)]) - { - // PITA dynamic invocation for 10.4 builds.. - NSInvocation* inv = [NSInvocation invocationWithMethodSignature: [view methodSignatureForSelector: @selector (setAlphaValue:)]]; - [inv setSelector: @selector (setAlphaValue:)]; - [inv setTarget: view]; - CGFloat cgNewAlpha = (CGFloat) newAlpha; - [inv setArgument: &cgNewAlpha atIndex: 2]; - [inv invoke]; - } - #endif - } + else + [window setAlphaValue: (CGFloat) newAlpha]; } void setMinimised (bool shouldBeMinimised) override @@ -673,9 +658,13 @@ public: if (invScale > 0.0f) handleMagnifyGesture (0, getMousePos (ev, view), getMouseTime (ev), 1.0f / invScale); #endif - (void) ev; + ignoreUnused (ev); } + void redirectCopy (NSObject*) { handleKeyPress (KeyPress ('c', ModifierKeys (ModifierKeys::commandModifier), 'c')); } + void redirectPaste (NSObject*) { handleKeyPress (KeyPress ('v', ModifierKeys (ModifierKeys::commandModifier), 'v')); } + void redirectCut (NSObject*) { handleKeyPress (KeyPress ('x', ModifierKeys (ModifierKeys::commandModifier), 'x')); } + void sendMouseEvent (NSEvent* ev) { updateModifiers (ev); @@ -782,16 +771,6 @@ public: handleModifierKeysChange(); } - #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 - bool redirectPerformKeyEquivalent (NSEvent* ev) - { - if ([ev type] == NSKeyDown) return redirectKeyDown (ev); - if ([ev type] == NSKeyUp) return redirectKeyUp (ev); - - return false; - } - #endif - void drawRect (NSRect r) { if (r.size.width < 1.0f || r.size.height < 1.0f) @@ -1475,10 +1454,6 @@ struct JuceNSViewClass : public ObjCClass addMethod (@selector (resignFirstResponder), resignFirstResponder, "c@:"); addMethod (@selector (acceptsFirstResponder), acceptsFirstResponder, "c@:"); - #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 - addMethod (@selector (performKeyEquivalent:), performKeyEquivalent, "c@:@"); - #endif - addMethod (@selector (draggingEntered:), draggingEntered, @encode (NSDragOperation), "@:@"); addMethod (@selector (draggingUpdated:), draggingUpdated, @encode (NSDragOperation), "@:@"); addMethod (@selector (draggingEnded:), draggingEnded, "v@:@"); @@ -1487,6 +1462,10 @@ struct JuceNSViewClass : public ObjCClass addMethod (@selector (performDragOperation:), performDragOperation, "c@:@"); addMethod (@selector (concludeDragOperation:), concludeDragOperation, "v@:@"); + addMethod (@selector (paste:), paste, "v@:@"); + addMethod (@selector (copy:), copy, "v@:@"); + addMethod (@selector (cut:), cut, "v@:@"); + addProtocol (@protocol (NSTextInput)); registerClass(); @@ -1532,6 +1511,9 @@ private: static void mouseExited (id self, SEL, NSEvent* ev) { if (NSViewComponentPeer* const p = getOwner (self)) p->redirectMouseExit (ev); } static void scrollWheel (id self, SEL, NSEvent* ev) { if (NSViewComponentPeer* const p = getOwner (self)) p->redirectMouseWheel (ev); } static void magnify (id self, SEL, NSEvent* ev) { if (NSViewComponentPeer* const p = getOwner (self)) p->redirectMagnify (ev); } + static void copy (id self, SEL, NSObject* s) { if (NSViewComponentPeer* const p = getOwner (self)) p->redirectCopy (s); } + static void paste (id self, SEL, NSObject* s) { if (NSViewComponentPeer* const p = getOwner (self)) p->redirectPaste (s); } + static void cut (id self, SEL, NSObject* s) { if (NSViewComponentPeer* const p = getOwner (self)) p->redirectCut (s); } static BOOL acceptsFirstMouse (id, SEL, NSEvent*) { return YES; } @@ -1706,18 +1688,6 @@ private: owner->redirectModKeyChange (ev); } - #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 - static BOOL performKeyEquivalent (id self, SEL, NSEvent* ev) - { - if (NSViewComponentPeer* const owner = getOwner (self)) - if (owner->redirectPerformKeyEquivalent (ev)) - return true; - - objc_super s = { self, [NSView class] }; - return getMsgSendSuperFn() (&s, @selector (performKeyEquivalent:), ev) != nil; - } - #endif - static BOOL becomeFirstResponder (id self, SEL) { if (NSViewComponentPeer* const owner = getOwner (self)) @@ -2028,7 +1998,7 @@ void Desktop::setKioskComponent (Component* kioskComp, bool shouldBeEnabled, boo SetSystemUIMode (kUIModeNormal, 0); } #else - (void) kioskComp; (void) shouldBeEnabled; (void) allowMenusAndBars; + ignoreUnused (kioskComp, shouldBeEnabled, allowMenusAndBars); // If you're targeting OSes earlier than 10.6 and want to use this feature, // you'll need to enable JUCE_SUPPORT_CARBON. diff --git a/source/modules/juce_gui_basics/native/juce_mac_Windowing.mm b/source/modules/juce_gui_basics/native/juce_mac_Windowing.mm index 103f4b149..ced9b0f7a 100644 --- a/source/modules/juce_gui_basics/native/juce_mac_Windowing.mm +++ b/source/modules/juce_gui_basics/native/juce_mac_Windowing.mm @@ -270,7 +270,7 @@ public: kIOPMAssertionLevelOn, CFSTR ("JUCE Playback"), &assertionID); - jassert (res == kIOReturnSuccess); (void) res; + jassert (res == kIOReturnSuccess); ignoreUnused (res); } ~PMAssertion() @@ -330,7 +330,7 @@ public: static void displayReconfigurationCallBack (CGDirectDisplayID, CGDisplayChangeSummaryFlags, void*) { - const_cast (Desktop::getInstance().getDisplays()).refresh(); + const_cast (Desktop::getInstance().getDisplays()).refresh(); } juce_DeclareSingleton_SingleThreaded_Minimal (DisplaySettingsChangeCallback) @@ -443,7 +443,7 @@ void Process::setDockIconVisible (bool isVisible) [NSApp setActivationPolicy: isVisible ? NSApplicationActivationPolicyRegular : NSApplicationActivationPolicyProhibited]; #else - (void) isVisible; + ignoreUnused (isVisible); jassertfalse; // sorry, not available in 10.5! #endif } diff --git a/source/modules/juce_gui_basics/native/juce_win32_DragAndDrop.cpp b/source/modules/juce_gui_basics/native/juce_win32_DragAndDrop.cpp index 253364ebe..0e25ac386 100644 --- a/source/modules/juce_gui_basics/native/juce_win32_DragAndDrop.cpp +++ b/source/modules/juce_gui_basics/native/juce_win32_DragAndDrop.cpp @@ -282,7 +282,7 @@ bool DragAndDropContainer::performExternalDragDropOfText (const String& text) const size_t numBytes = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer()); medium.hGlobal = GlobalAlloc (GMEM_MOVEABLE | GMEM_ZEROINIT, numBytes + 2); - WCHAR* const data = static_cast (GlobalLock (medium.hGlobal)); + WCHAR* const data = static_cast (GlobalLock (medium.hGlobal)); text.copyToUTF16 (data, numBytes); format.cfFormat = CF_UNICODETEXT; diff --git a/source/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp b/source/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp index 0e5d3abe5..f610dfb75 100644 --- a/source/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp +++ b/source/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp @@ -225,7 +225,7 @@ void FileChooser::showPlatformDialog (Array& results, const String& title_ filter.copyToUTF16 (filters + (bytesWritten / sizeof (WCHAR)), ((filterSpaceNumChars - 1) * sizeof (WCHAR) - bytesWritten)); - for (int i = 0; i < filterSpaceNumChars; ++i) + for (size_t i = 0; i < filterSpaceNumChars; ++i) if (filters[i] == '|') filters[i] = 0; diff --git a/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp b/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp index df708e312..7595e9c15 100644 --- a/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp +++ b/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp @@ -39,14 +39,6 @@ #define WM_APPCOMMAND 0x0319 #endif -#ifndef MI_WP_SIGNATURE - #define MI_WP_SIGNATURE 0xFF515700 -#endif - -#ifndef SIGNATURE_MASK - #define SIGNATURE_MASK 0xFFFFFF00 -#endif - extern void juce_repeatLastProcessPriority(); extern void juce_checkCurrentlyFocusedTopLevelWindow(); // in juce_TopLevelWindow.cpp extern bool juce_isRunningInWine(); @@ -73,7 +65,6 @@ bool Desktop::canUseSemiTransparentWindows() noexcept //============================================================================== #ifndef WM_TOUCH #define WM_TOUCH 0x0240 - #define TOUCH_COORD_TO_PIXEL(l) ((l) / 100) #define TOUCHEVENTF_MOVE 0x0001 #define TOUCHEVENTF_DOWN 0x0002 #define TOUCHEVENTF_UP 0x0004 @@ -1129,19 +1120,16 @@ public: JUCE_DECLARE_NON_COPYABLE (JuceDropTarget) }; - #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client static bool offerKeyMessageToJUCEWindow (MSG& m) { if (m.message == WM_KEYDOWN || m.message == WM_KEYUP) if (Component::getCurrentlyFocusedComponent() != nullptr) if (HWNDComponentPeer* h = getOwnerOfWindow (m.hwnd)) - if (m.message == WM_KEYDOWN ? h->doKeyDown (m.wParam) - : h->doKeyUp (m.wParam)) - return true; + return m.message == WM_KEYDOWN ? h->doKeyDown (m.wParam) + : h->doKeyUp (m.wParam); return false; } - #endif private: HWND hwnd, parentToAddTo; @@ -1694,7 +1682,7 @@ private: void setCurrentRenderingEngine (int index) override { - (void) index; + ignoreUnused (index); #if JUCE_DIRECT2D if (getAvailableRenderingEngines().size() > 1) @@ -1719,12 +1707,14 @@ private: if (registerTouchWindow == nullptr) return false; - LPARAM dw = GetMessageExtraInfo(); - // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms703320(v=vs.85).aspx - return (dw & SIGNATURE_MASK) == MI_WP_SIGNATURE; + // Relevent info about touch/pen detection flags: + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms703320(v=vs.85).aspx + // http://www.petertissen.de/?p=4 + + return (GetMessageExtraInfo() & 0xFFFFFF80 /*SIGNATURE_MASK*/) == 0xFF515780 /*MI_WP_SIGNATURE*/; } - void doMouseMove (Point position) + void doMouseMove (Point position, bool isMouseDownEvent) { // this will be handled by WM_TOUCH if (isTouchEvent()) @@ -1733,7 +1723,13 @@ private: if (! isMouseOver) { isMouseOver = true; - ModifierKeys::getCurrentModifiersRealtime(); // (This avoids a rare stuck-button problem when focus is lost unexpectedly) + + // This avoids a rare stuck-button problem when focus is lost unexpectedly, but must + // not be called as part of a move, in case it's actually a mouse-drag from another + // app which ends up here when we get focus before the mouse is released.. + if (isMouseDownEvent) + ModifierKeys::getCurrentModifiersRealtime(); + updateKeyModifiers(); TRACKMOUSEEVENT tme; @@ -1773,7 +1769,7 @@ private: if (GetCapture() != hwnd) SetCapture (hwnd); - doMouseMove (position); + doMouseMove (position, true); if (isValidPeer (this)) { @@ -1923,8 +1919,8 @@ private: bool isCancel = false; const int touchIndex = currentTouches.getIndexOfTouch (touch.dwID); const int64 time = getMouseEventTime(); - const Point pos (globalToLocal (Point (static_cast (TOUCH_COORD_TO_PIXEL (touch.x)), - static_cast (TOUCH_COORD_TO_PIXEL (touch.y))))); + const Point pos (globalToLocal (Point (touch.x / 100.0f, + touch.y / 100.0f))); const float pressure = MouseInputSource::invalidPressure; ModifierKeys modsToSend (currentModifiers); @@ -1934,7 +1930,7 @@ private: modsToSend = currentModifiers; // this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before. - handleMouseEvent (touchIndex, pos.toFloat(), modsToSend.withoutMouseButtons(), pressure, time); + handleMouseEvent (touchIndex, pos, modsToSend.withoutMouseButtons(), pressure, time); if (! isValidPeer (this)) // (in case this component was deleted by the event) return false; @@ -1958,7 +1954,7 @@ private: currentModifiers = currentModifiers.withoutMouseButtons(); } - handleMouseEvent (touchIndex, pos.toFloat(), modsToSend, pressure, time); + handleMouseEvent (touchIndex, pos, modsToSend, pressure, time); if (! isValidPeer (this)) // (in case this component was deleted by the event) return false; @@ -2450,7 +2446,7 @@ private: return 1; //============================================================================== - case WM_MOUSEMOVE: doMouseMove (getPointFromLParam (lParam)); return 0; + case WM_MOUSEMOVE: doMouseMove (getPointFromLParam (lParam), false); return 0; case WM_MOUSELEAVE: doMouseExit(); return 0; case WM_LBUTTONDOWN: @@ -2757,7 +2753,7 @@ private: reset(); if (TextInputTarget* const target = owner.findCurrentTextInputTarget()) - target->insertTextAtCaret (String::empty); + target->insertTextAtCaret (String()); } void handleEndComposition (ComponentPeer& owner, HWND hWnd) @@ -2768,7 +2764,7 @@ private: if (TextInputTarget* const target = owner.findCurrentTextInputTarget()) { target->setHighlightedRegion (compositionRange); - target->insertTextAtCaret (String::empty); + target->insertTextAtCaret (String()); compositionRange.setLength (0); target->setHighlightedRegion (Range::emptyRange (compositionRange.getEnd())); @@ -2843,7 +2839,7 @@ private: return String (buffer); } - return String::empty; + return String(); } int getCompositionCaretPos (HIMC hImc, LPARAM lParam, const String& currentIMEString) const @@ -3021,9 +3017,8 @@ bool KeyPress::isKeyCurrentlyDown (const int keyCode) return HWNDComponentPeer::isKeyDown (k); } -#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client +// (This internal function is used by the plugin client module) bool offerKeyMessageToJUCEWindow (MSG& m) { return HWNDComponentPeer::offerKeyMessageToJUCEWindow (m); } -#endif //============================================================================== bool JUCE_CALLTYPE Process::isForegroundProcess() diff --git a/source/modules/juce_gui_basics/positioning/juce_RelativeCoordinate.cpp b/source/modules/juce_gui_basics/positioning/juce_RelativeCoordinate.cpp index 62d3d3d98..a545acd7a 100644 --- a/source/modules/juce_gui_basics/positioning/juce_RelativeCoordinate.cpp +++ b/source/modules/juce_gui_basics/positioning/juce_RelativeCoordinate.cpp @@ -69,13 +69,13 @@ RelativeCoordinate& RelativeCoordinate::operator= (const RelativeCoordinate& oth #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS RelativeCoordinate::RelativeCoordinate (RelativeCoordinate&& other) noexcept - : term (static_cast (other.term)) + : term (static_cast (other.term)) { } RelativeCoordinate& RelativeCoordinate::operator= (RelativeCoordinate&& other) noexcept { - term = static_cast (other.term); + term = static_cast (other.term); return *this; } #endif diff --git a/source/modules/juce_gui_basics/positioning/juce_RelativeRectangle.cpp b/source/modules/juce_gui_basics/positioning/juce_RelativeRectangle.cpp index 7cd71236c..009bb83f4 100644 --- a/source/modules/juce_gui_basics/positioning/juce_RelativeRectangle.cpp +++ b/source/modules/juce_gui_basics/positioning/juce_RelativeRectangle.cpp @@ -244,7 +244,7 @@ void RelativeRectangle::applyToComponent (Component& component) const { if (isDynamic()) { - RelativeRectangleComponentPositioner* current = dynamic_cast (component.getPositioner()); + RelativeRectangleComponentPositioner* current = dynamic_cast (component.getPositioner()); if (current == nullptr || ! current->isUsingRectangle (*this)) { diff --git a/source/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.cpp b/source/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.cpp index 914591862..b886de928 100644 --- a/source/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.cpp +++ b/source/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.cpp @@ -45,7 +45,7 @@ public: void setValue (const var& newValue) { - const var remappedVal (mappings [static_cast (newValue) - 1]); + const var remappedVal (mappings [static_cast (newValue) - 1]); if (! remappedVal.equalsWithSameType (sourceValue)) sourceValue = remappedVal; diff --git a/source/modules/juce_gui_basics/widgets/juce_Label.cpp b/source/modules/juce_gui_basics/widgets/juce_Label.cpp index d9f5cab0a..2fb4b6d0a 100644 --- a/source/modules/juce_gui_basics/widgets/juce_Label.cpp +++ b/source/modules/juce_gui_basics/widgets/juce_Label.cpp @@ -391,7 +391,7 @@ public: static Component* getComp (Component* current) { - return dynamic_cast (current) != nullptr + return dynamic_cast (current) != nullptr ? current->getParentComponent() : current; } }; @@ -460,7 +460,7 @@ void Label::textEditorEscapeKeyPressed (TextEditor& ed) if (editor != nullptr) { jassert (&ed == editor); - (void) ed; + ignoreUnused (ed); editor->setText (textValue.toString(), false); hideEditor (true); diff --git a/source/modules/juce_gui_basics/widgets/juce_ListBox.cpp b/source/modules/juce_gui_basics/widgets/juce_ListBox.cpp index 279bda4f0..91487420b 100644 --- a/source/modules/juce_gui_basics/widgets/juce_ListBox.cpp +++ b/source/modules/juce_gui_basics/widgets/juce_ListBox.cpp @@ -951,7 +951,7 @@ void ListBox::startDragAndDrop (const MouseEvent& e, const SparseSet& rowsT //============================================================================== Component* ListBoxModel::refreshComponentForRow (int, bool, Component* existingComponentToUpdate) { - (void) existingComponentToUpdate; + ignoreUnused (existingComponentToUpdate); jassert (existingComponentToUpdate == nullptr); // indicates a failure in the code that recycles the components return nullptr; } diff --git a/source/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.cpp b/source/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.cpp index aa014a40d..96482c776 100644 --- a/source/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.cpp +++ b/source/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.cpp @@ -445,7 +445,7 @@ String TableHeaderComponent::toString() const void TableHeaderComponent::restoreFromString (const String& storedVersion) { - ScopedPointer storedXml (XmlDocument::parse (storedVersion)); + ScopedPointer storedXml (XmlDocument::parse (storedVersion)); int index = 0; if (storedXml != nullptr && storedXml->hasTagName ("TABLELAYOUT")) diff --git a/source/modules/juce_gui_basics/widgets/juce_TableListBox.cpp b/source/modules/juce_gui_basics/widgets/juce_TableListBox.cpp index 3cfbc19b8..541f424d3 100644 --- a/source/modules/juce_gui_basics/widgets/juce_TableListBox.cpp +++ b/source/modules/juce_gui_basics/widgets/juce_TableListBox.cpp @@ -470,7 +470,7 @@ var TableListBoxModel::getDragSourceDescription (const SparseSet&) Component* TableListBoxModel::refreshComponentForCell (int, int, bool, Component* existingComponentToUpdate) { - (void) existingComponentToUpdate; + ignoreUnused (existingComponentToUpdate); jassert (existingComponentToUpdate == nullptr); // indicates a failure in the code that recycles the components return nullptr; } diff --git a/source/modules/juce_gui_basics/widgets/juce_TextEditor.h b/source/modules/juce_gui_basics/widgets/juce_TextEditor.h index cbf2ee97d..6089db6b0 100644 --- a/source/modules/juce_gui_basics/widgets/juce_TextEditor.h +++ b/source/modules/juce_gui_basics/widgets/juce_TextEditor.h @@ -328,7 +328,7 @@ public: @param newText the text to add @param sendTextChangeMessage if true, this will cause a change message to be sent to all the listeners. - @see insertText + @see insertTextAtCaret */ void setText (const String& newText, bool sendTextChangeMessage = true); diff --git a/source/modules/juce_gui_basics/widgets/juce_ToolbarItemComponent.cpp b/source/modules/juce_gui_basics/widgets/juce_ToolbarItemComponent.cpp index 7011af5e7..5e5ce3f2e 100644 --- a/source/modules/juce_gui_basics/widgets/juce_ToolbarItemComponent.cpp +++ b/source/modules/juce_gui_basics/widgets/juce_ToolbarItemComponent.cpp @@ -109,7 +109,7 @@ private: ToolbarItemComponent* getToolbarItemComponent() const noexcept { - return dynamic_cast (getParentComponent()); + return dynamic_cast (getParentComponent()); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemDragAndDropOverlayComponent) @@ -141,7 +141,7 @@ ToolbarItemComponent::~ToolbarItemComponent() Toolbar* ToolbarItemComponent::getToolbar() const { - return dynamic_cast (getParentComponent()); + return dynamic_cast (getParentComponent()); } bool ToolbarItemComponent::isToolbarVertical() const diff --git a/source/modules/juce_gui_basics/widgets/juce_ToolbarItemComponent.h b/source/modules/juce_gui_basics/widgets/juce_ToolbarItemComponent.h index 8b6881fc6..0594e5787 100644 --- a/source/modules/juce_gui_basics/widgets/juce_ToolbarItemComponent.h +++ b/source/modules/juce_gui_basics/widgets/juce_ToolbarItemComponent.h @@ -193,7 +193,7 @@ private: const int itemId; ToolbarEditingMode mode; Toolbar::ToolbarItemStyle toolbarStyle; - ScopedPointer overlayComp; + ScopedPointer overlayComp; int dragOffsetX, dragOffsetY; bool isActive, isBeingDragged, isBeingUsedAsAButton; Rectangle contentArea; diff --git a/source/modules/juce_gui_basics/widgets/juce_TreeView.cpp b/source/modules/juce_gui_basics/widgets/juce_TreeView.cpp index e78eb34f3..973808274 100644 --- a/source/modules/juce_gui_basics/widgets/juce_TreeView.cpp +++ b/source/modules/juce_gui_basics/widgets/juce_TreeView.cpp @@ -416,7 +416,7 @@ public: ContentComponent* getContentComp() const noexcept { - return static_cast (getViewedComponent()); + return static_cast (getViewedComponent()); } bool keyPressed (const KeyPress& key) override @@ -490,7 +490,7 @@ void TreeView::setRootItem (TreeViewItem* const newRootItem) void TreeView::deleteRootItem() { - const ScopedPointer deleter (rootItem); + const ScopedPointer deleter (rootItem); setRootItem (nullptr); } diff --git a/source/modules/juce_gui_basics/widgets/juce_TreeView.h b/source/modules/juce_gui_basics/widgets/juce_TreeView.h index 74de75840..1ab9c0c75 100644 --- a/source/modules/juce_gui_basics/widgets/juce_TreeView.h +++ b/source/modules/juce_gui_basics/widgets/juce_TreeView.h @@ -573,7 +573,7 @@ public: private: TreeViewItem& treeViewItem; - ScopedPointer oldOpenness; + ScopedPointer oldOpenness; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpennessRestorer) }; diff --git a/source/modules/juce_gui_basics/windows/juce_AlertWindow.cpp b/source/modules/juce_gui_basics/windows/juce_AlertWindow.cpp index 625b95aba..19c946882 100644 --- a/source/modules/juce_gui_basics/windows/juce_AlertWindow.cpp +++ b/source/modules/juce_gui_basics/windows/juce_AlertWindow.cpp @@ -601,7 +601,7 @@ private: else #endif { - (void) modal; // (to avoid an unused variable warning) + ignoreUnused (modal); alertBox->enterModalState (true, callback, true); alertBox.release(); diff --git a/source/modules/juce_gui_basics/windows/juce_AlertWindow.h b/source/modules/juce_gui_basics/windows/juce_AlertWindow.h index d72dcb57c..9416e2661 100644 --- a/source/modules/juce_gui_basics/windows/juce_AlertWindow.h +++ b/source/modules/juce_gui_basics/windows/juce_AlertWindow.h @@ -131,7 +131,7 @@ public: */ void addTextEditor (const String& name, const String& initialContents, - const String& onScreenLabel = String::empty, + const String& onScreenLabel = String(), bool isPasswordBox = false); /** Returns the contents of a named textbox. @@ -161,7 +161,7 @@ public: */ void addComboBox (const String& name, const StringArray& items, - const String& onScreenLabel = String::empty); + const String& onScreenLabel = String()); /** Returns a drop-down list that was added to the AlertWindow. @@ -246,7 +246,7 @@ public: static void JUCE_CALLTYPE showMessageBox (AlertIconType iconType, const String& title, const String& message, - const String& buttonText = String::empty, + const String& buttonText = String(), Component* associatedComponent = nullptr); #endif @@ -274,7 +274,7 @@ public: static void JUCE_CALLTYPE showMessageBoxAsync (AlertIconType iconType, const String& title, const String& message, - const String& buttonText = String::empty, + const String& buttonText = String(), Component* associatedComponent = nullptr, ModalComponentManager::Callback* callback = nullptr); @@ -317,8 +317,8 @@ public: const String& title, const String& message, #if JUCE_MODAL_LOOPS_PERMITTED - const String& button1Text = String::empty, - const String& button2Text = String::empty, + const String& button1Text = String(), + const String& button2Text = String(), Component* associatedComponent = nullptr, ModalComponentManager::Callback* callback = nullptr); #else @@ -371,9 +371,9 @@ public: const String& title, const String& message, #if JUCE_MODAL_LOOPS_PERMITTED - const String& button1Text = String::empty, - const String& button2Text = String::empty, - const String& button3Text = String::empty, + const String& button1Text = String(), + const String& button2Text = String(), + const String& button3Text = String(), Component* associatedComponent = nullptr, ModalComponentManager::Callback* callback = nullptr); #else diff --git a/source/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp b/source/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp index cb71b2198..324b9d274 100644 --- a/source/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp +++ b/source/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp @@ -177,11 +177,16 @@ Component* ComponentPeer::getTargetForKeyPress() bool ComponentPeer::handleKeyPress (const int keyCode, const juce_wchar textCharacter) { ModifierKeys::updateCurrentModifiers(); - bool keyWasUsed = false; - const KeyPress keyInfo (keyCode, - ModifierKeys::getCurrentModifiers().withoutMouseButtons(), - textCharacter); + return handleKeyPress (KeyPress (keyCode, + ModifierKeys::getCurrentModifiers().withoutMouseButtons(), + textCharacter)); +} + + +bool ComponentPeer::handleKeyPress (const KeyPress& keyInfo) +{ + bool keyWasUsed = false; for (Component* target = getTargetForKeyPress(); target != nullptr; target = target->getParentComponent()) { @@ -588,4 +593,4 @@ void ComponentPeer::setRepresentedFile (const File&) //============================================================================== int ComponentPeer::getCurrentRenderingEngine() const { return 0; } -void ComponentPeer::setCurrentRenderingEngine (int index) { jassert (index == 0); (void) index; } +void ComponentPeer::setCurrentRenderingEngine (int index) { jassert (index == 0); ignoreUnused (index); } diff --git a/source/modules/juce_gui_basics/windows/juce_ComponentPeer.h b/source/modules/juce_gui_basics/windows/juce_ComponentPeer.h index a79a1fbd0..427761899 100644 --- a/source/modules/juce_gui_basics/windows/juce_ComponentPeer.h +++ b/source/modules/juce_gui_basics/windows/juce_ComponentPeer.h @@ -269,6 +269,11 @@ public: */ bool handleKeyPress (int keyCode, juce_wchar textCharacter); + /** Called when a key is pressed. + Returns true if the keystroke was used. + */ + bool handleKeyPress (const KeyPress& key); + /** Called whenever a key is pressed or released. Returns true if the keystroke was used. */ diff --git a/source/modules/juce_gui_basics/windows/juce_DialogWindow.h b/source/modules/juce_gui_basics/windows/juce_DialogWindow.h index 9c4bdf334..6064e46a3 100644 --- a/source/modules/juce_gui_basics/windows/juce_DialogWindow.h +++ b/source/modules/juce_gui_basics/windows/juce_DialogWindow.h @@ -93,7 +93,7 @@ public: you'd like the dialog to automatically delete the component when the dialog has terminated. */ - OptionalScopedPointer content; + OptionalScopedPointer content; /** If this is not a nullptr, it indicates a component that you'd like to position this dialog box in front of. See the DocumentWindow::centreAroundComponent() method for diff --git a/source/modules/juce_gui_basics/windows/juce_DocumentWindow.cpp b/source/modules/juce_gui_basics/windows/juce_DocumentWindow.cpp index a397a3651..e94641cbf 100644 --- a/source/modules/juce_gui_basics/windows/juce_DocumentWindow.cpp +++ b/source/modules/juce_gui_basics/windows/juce_DocumentWindow.cpp @@ -188,15 +188,6 @@ void DocumentWindow::paint (Graphics& g) { ResizableWindow::paint (g); - if (resizableBorder == nullptr) - { - RectangleList border (getLocalBounds()); - border.subtract (getBorderThickness().subtractedFrom (getLocalBounds())); - - g.setColour (getBackgroundColour().overlaidWith (Colour (0x80000000))); - g.fillRectList (border); - } - const Rectangle titleBarArea (getTitleBarArea()); g.reduceClipRegion (titleBarArea); g.setOrigin (titleBarArea.getPosition()); diff --git a/source/modules/juce_gui_basics/windows/juce_DocumentWindow.h b/source/modules/juce_gui_basics/windows/juce_DocumentWindow.h index 439fa15fd..90972c2c3 100644 --- a/source/modules/juce_gui_basics/windows/juce_DocumentWindow.h +++ b/source/modules/juce_gui_basics/windows/juce_DocumentWindow.h @@ -277,9 +277,9 @@ private: //============================================================================== int titleBarHeight, menuBarHeight, requiredButtons; bool positionTitleBarButtonsOnLeft, drawTitleTextCentred; - ScopedPointer