diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 63b2e64..9696e62 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -28,12 +28,12 @@ mac-builder: paths: - build/install-x64/* script: - - mkdir -p build; cd build; - - cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -D"CMAKE_INSTALL_PREFIX:PATH=install-x64" -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -D"CMAKE_BUILD_TYPE:STRING=Release" -D"CMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk" -D"CMAKE_OSX_DEPLOYMENT_TARGET=10.9" -stdlib=libc++ ../ - - make - - make install - - echo -e "CI_PROJECT_NAME:$CI_PROJECT_NAME\nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME\nCI_COMMIT_SHA:$CI_COMMIT_SHA\nCI_JOB_ID:$CI_JOB_ID" > "install-x64/share/$CI_PROJECT_NAME" - - git log $(git describe --tags --abbrev=0 @^)..@ --oneline --pretty=format:"%C(auto,yellow)%h%C(auto,magenta)% %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(25,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D" --date=short > "install-x64/share/$CI_PROJECT_NAME.log" + - mkdir -p build + - cmake -B build -S . -DCMAKE_EXE_LINKER_FLAGS="-stdlib=libc++" -DCMAKE_SHARED_LINKER_FLAGS="-stdlib=libc++" -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -D"CMAKE_INSTALL_PREFIX:PATH=build/install-x64" -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -D"CMAKE_BUILD_TYPE:STRING=Release" -D"CMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk" -D"CMAKE_OSX_DEPLOYMENT_TARGET=10.9" + - cmake --build build + - cmake --install build + - echo -e "CI_PROJECT_NAME:$CI_PROJECT_NAME\nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME\nCI_COMMIT_SHA:$CI_COMMIT_SHA\nCI_JOB_ID:$CI_JOB_ID" > "build/install-x64/share/$CI_PROJECT_NAME" + - git log $(git describe --tags --abbrev=0 @^)..@ --oneline --pretty=format:"%C(auto,yellow)%h%C(auto,magenta)% %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(25,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D" --date=short > "build/install-x64/share/$CI_PROJECT_NAME.log" when: always except: - tags @@ -52,13 +52,12 @@ windows-builder-x64: - $env:ZMQDIR = "C:\msys64\usr" - $env:Path = "C:\msys64\mingw64\bin;C:\msys64\mingw64\lib;C:\msys64\usr\lib\cmake\UnitTest++;C:\msys64\home\jonathan\depot_tools;C:\msys64\usr;C:\msys64\usr\lib;" + $env:Path; - New-Item -ItemType Directory -Force -Path build - - cd build - - cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -D"CMAKE_INSTALL_PREFIX:PATH=install-x64" -G "MSYS Makefiles" -DCMAKE_MAKE_PROGRAM=mingw32-make -D"CMAKE_BUILD_TYPE:STRING=Release" ../ - - mingw32-make - - mingw32-make install - - New-Item -path "install-x64/share/" -Name "$CI_PROJECT_NAME" -Value "CI_PROJECT_NAME:$CI_PROJECT_NAME`nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME`nCI_COMMIT_SHA:$CI_COMMIT_SHA`nCI_JOB_ID:$CI_JOB_ID" -ItemType file -force + - cmake -B build -S . -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -D"CMAKE_INSTALL_PREFIX:PATH=build/install-x64" -G "MSYS Makefiles" -DCMAKE_MAKE_PROGRAM=mingw32-make -D"CMAKE_BUILD_TYPE:STRING=Release" + - cmake --build build + - cmake --install build + - New-Item -path "build/install-x64/share/" -Name "$CI_PROJECT_NAME" -Value "CI_PROJECT_NAME:$CI_PROJECT_NAME`nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME`nCI_COMMIT_SHA:$CI_COMMIT_SHA`nCI_JOB_ID:$CI_JOB_ID" -ItemType file -force - $PREV_GIT_LABEL=(git describe --tags --abbrev=0 '@^') - - git log "$PREV_GIT_LABEL..@" --oneline --pretty=format:"%C(auto,yellow)%h%C(auto,magenta)% %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(25,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D" --date=short > "install-x64/share/$CI_PROJECT_NAME.log" + - git log "$PREV_GIT_LABEL..@" --oneline --pretty=format:"%C(auto,yellow)%h%C(auto,magenta)% %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(25,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D" --date=short > "build/install-x64/share/$CI_PROJECT_NAME.log" when: always except: - tags @@ -77,13 +76,12 @@ windows-builder-x86: - $env:ZMQDIR = "C:\msys32\usr" - $env:Path = "C:\msys32\mingw32\bin;C:\msys32\mingw32\lib;C:\msys32\usr\lib\cmake\UnitTest++;C:\msys32\home\jonathan\depot_tools;C:\msys32\usr;C:\msys32\usr\lib;" + $env:Path; - New-Item -ItemType Directory -Force -Path build - - cd build - - cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -D"CMAKE_INSTALL_PREFIX:PATH=install-x86" -G "MSYS Makefiles" -DCMAKE_MAKE_PROGRAM=mingw32-make -D"CMAKE_BUILD_TYPE:STRING=Release" -D"CMAKE_CXX_FLAGS=-m32" -D"CMAKE_C_FLAGS=-m32" ../ - - mingw32-make - - mingw32-make install - - New-Item -path "install-x86/share/" -Name "$CI_PROJECT_NAME" -Value "CI_PROJECT_NAME:$CI_PROJECT_NAME`nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME`nCI_COMMIT_SHA:$CI_COMMIT_SHA`nCI_JOB_ID:$CI_JOB_ID" -ItemType file -force + - cmake -B build -s . -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -D"CMAKE_INSTALL_PREFIX:PATH=build/install-x86" -G "MSYS Makefiles" -DCMAKE_MAKE_PROGRAM=mingw32-make -D"CMAKE_BUILD_TYPE:STRING=Release" -D"CMAKE_CXX_FLAGS=-m32" -D"CMAKE_C_FLAGS=-m32" + - cmake --build build + - cmake --install build + - New-Item -path "build/install-x86/share/" -Name "$CI_PROJECT_NAME" -Value "CI_PROJECT_NAME:$CI_PROJECT_NAME`nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME`nCI_COMMIT_SHA:$CI_COMMIT_SHA`nCI_JOB_ID:$CI_JOB_ID" -ItemType file -force - $PREV_GIT_LABEL=(git describe --tags --abbrev=0 '@^') - - git log "$PREV_GIT_LABEL..@" --oneline --pretty=format:"%C(auto,yellow)%h%C(auto,magenta)% %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(25,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D" --date=short > "install-x86/share/$CI_PROJECT_NAME.log" + - git log "$PREV_GIT_LABEL..@" --oneline --pretty=format:"%C(auto,yellow)%h%C(auto,magenta)% %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(25,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D" --date=short > "build/install-x86/share/$CI_PROJECT_NAME.log" when: always except: - tags @@ -99,4 +97,4 @@ trigger-pipeline: except: - tags tags: - - linux \ No newline at end of file + - linux diff --git a/CMakeLists.txt b/CMakeLists.txt index e0b61ca..c98a9bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,10 +83,11 @@ else() endif() endif() -message("\ -Generating build files for ${PROJECT_NAME} \ -version ${PROJECT_VERSION_FULL} (${PROJECT_VERSION_HEX})" ) -message("SO/API/ABI Version: ${PROJECT_SO_VERSION}" ) +message(" +Generating build files for OpenShot with CMake ${CMAKE_VERSION} + Building ${PROJECT_NAME} (version ${PROJECT_VERSION_FULL} (${PROJECT_VERSION_HEX}) + SO/API/ABI Version: ${PROJECT_SO_VERSION} +") # Define install paths according to system conventions diff --git a/JuceLibraryCode/AppConfig.h b/JuceLibraryCode/AppConfig.h index 9715e1b..8a7649b 100644 --- a/JuceLibraryCode/AppConfig.h +++ b/JuceLibraryCode/AppConfig.h @@ -40,13 +40,15 @@ #endif #ifndef JUCE_REPORT_APP_USAGE - #define JUCE_REPORT_APP_USAGE 1 + #define JUCE_REPORT_APP_USAGE 0 #endif // END SECTION A #define JUCE_USE_DARK_SPLASH_SCREEN 1 +#define JUCE_PROJUCER_VERSION 0x50407 + //============================================================================== #define JUCE_MODULE_AVAILABLE_juce_audio_basics 1 #define JUCE_MODULE_AVAILABLE_juce_audio_devices 1 @@ -143,7 +145,7 @@ #endif #ifndef JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES - //#define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES 0 + #define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES 1 #endif #ifndef JUCE_INCLUDE_ZLIB_CODE @@ -151,7 +153,7 @@ #endif #ifndef JUCE_USE_CURL - #define JUCE_USE_CURL 0 + #define JUCE_USE_CURL 0 #endif #ifndef JUCE_LOAD_CURL_SYMBOLS_LAZILY @@ -159,11 +161,11 @@ #endif #ifndef JUCE_CATCH_UNHANDLED_EXCEPTIONS - //#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 1 + //#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 0 #endif #ifndef JUCE_ALLOW_STATIC_NULL_VARIABLES - //#define JUCE_ALLOW_STATIC_NULL_VARIABLES 1 + //#define JUCE_ALLOW_STATIC_NULL_VARIABLES 0 #endif #ifndef JUCE_STRICT_REFCOUNTEDPOINTER @@ -173,8 +175,8 @@ //============================================================================== // juce_events flags: -#ifndef JUCE_EXECUTE_APP_SUSPEND_ON_IOS_BACKGROUND_TASK - //#define JUCE_EXECUTE_APP_SUSPEND_ON_IOS_BACKGROUND_TASK 0 +#ifndef JUCE_EXECUTE_APP_SUSPEND_ON_BACKGROUND_TASK + //#define JUCE_EXECUTE_APP_SUSPEND_ON_BACKGROUND_TASK 0 #endif //============================================================================== diff --git a/JuceLibraryCode/JuceHeader.h b/JuceLibraryCode/JuceHeader.h index 0e89d07..dc0ab1f 100644 --- a/JuceLibraryCode/JuceHeader.h +++ b/JuceLibraryCode/JuceHeader.h @@ -23,6 +23,16 @@ #include #undef Point + +#if defined (JUCE_PROJUCER_VERSION) && JUCE_PROJUCER_VERSION < JUCE_VERSION + /** If you've hit this error then the version of the Projucer that was used to generate this project is + older than the version of the JUCE modules being included. To fix this error, re-save your project + using the latest version of the Projucer or, if you aren't using the Projucer to manage your project, + remove the JUCE_PROJUCER_VERSION define from the AppConfig.h file. + */ + #error "This project was last saved using an outdated version of the Projucer! Re-save this project with the latest version to fix this error." +#endif + #if ! DONT_SET_USING_JUCE_NAMESPACE // If your code uses a lot of JUCE classes, then this will obviously save you // a lot of typing, but can be disabled by setting DONT_SET_USING_JUCE_NAMESPACE. @@ -34,7 +44,7 @@ namespace ProjectInfo { const char* const projectName = "libopenshot-audio"; const char* const companyName = "OpenShot Studios, LLC"; - const char* const versionString = "0.1.8"; - const int versionNumber = 0x108; + const char* const versionString = "0.2.0"; + const int versionNumber = 0x200; } #endif diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp index c4794af..40ba299 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp @@ -557,12 +557,17 @@ int JUCE_CALLTYPE AudioChannelSet::getAmbisonicOrderForNumChannels (int numChann return (static_cast (ambisonicOrder) == sqrtMinusOne ? ambisonicOrder : -1); } + +//============================================================================== //============================================================================== #if JUCE_UNIT_TESTS + class AudioChannelSetUnitTest : public UnitTest { public: - AudioChannelSetUnitTest() : UnitTest ("AudioChannelSetUnitTest", "Audio") {} + AudioChannelSetUnitTest() + : UnitTest ("AudioChannelSetUnitTest", UnitTestCategories::audio) + {} void runTest() override { @@ -645,6 +650,7 @@ private: }; static AudioChannelSetUnitTest audioChannelSetUnitTest; + #endif } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp index 162fd1a..a441a8f 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp @@ -460,13 +460,16 @@ void AudioDataConverters::deinterleaveSamples (const float* source, float** dest } +//============================================================================== //============================================================================== #if JUCE_UNIT_TESTS class AudioConversionTests : public UnitTest { public: - AudioConversionTests() : UnitTest ("Audio data conversion", "Audio") {} + AudioConversionTests() + : UnitTest ("Audio data conversion", UnitTestCategories::audio) + {} template struct Test5 @@ -480,7 +483,9 @@ public: static void test (UnitTest& unitTest, bool inPlace, Random& r) { const int numSamples = 2048; - int32 original[numSamples], converted[numSamples], reversed[numSamples]; + int32 original [(size_t) numSamples], + converted[(size_t) numSamples], + reversed [(size_t) numSamples]; { AudioData::Pointer d (original); diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h index 47edc64..e8e1fed 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h @@ -102,9 +102,9 @@ public: inline void advance() noexcept { ++data; } inline void skip (int numSamples) noexcept { data += numSamples; } - inline float getAsFloatLE() const noexcept { return (float) (*data * (1.0 / (1.0 + maxValue))); } + inline float getAsFloatLE() const noexcept { return (float) (*data * (1.0 / (1.0 + (double) maxValue))); } inline float getAsFloatBE() const noexcept { return getAsFloatLE(); } - inline void setAsFloatLE (float newValue) noexcept { *data = (int8) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue))); } + inline void setAsFloatLE (float newValue) noexcept { *data = (int8) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + (double) maxValue))); } inline void setAsFloatBE (float newValue) noexcept { setAsFloatLE (newValue); } inline int32 getAsInt32LE() const noexcept { return (int) (*((uint8*) data) << 24); } inline int32 getAsInt32BE() const noexcept { return getAsInt32LE(); } @@ -127,9 +127,9 @@ public: inline void advance() noexcept { ++data; } inline void skip (int numSamples) noexcept { data += numSamples; } - inline float getAsFloatLE() const noexcept { return (float) ((*data - 128) * (1.0 / (1.0 + maxValue))); } + inline float getAsFloatLE() const noexcept { return (float) ((*data - 128) * (1.0 / (1.0 + (double) maxValue))); } inline float getAsFloatBE() const noexcept { return getAsFloatLE(); } - inline void setAsFloatLE (float newValue) noexcept { *data = (uint8) jlimit (0, 255, 128 + roundToInt (newValue * (1.0 + maxValue))); } + inline void setAsFloatLE (float newValue) noexcept { *data = (uint8) jlimit (0, 255, 128 + roundToInt (newValue * (1.0 + (double) maxValue))); } inline void setAsFloatBE (float newValue) noexcept { setAsFloatLE (newValue); } inline int32 getAsInt32LE() const noexcept { return (int) (((uint8) (*data - 128)) << 24); } inline int32 getAsInt32BE() const noexcept { return getAsInt32LE(); } @@ -152,10 +152,10 @@ public: inline void advance() noexcept { ++data; } inline void skip (int numSamples) noexcept { data += numSamples; } - inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int16) ByteOrder::swapIfBigEndian (*data)); } - inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int16) ByteOrder::swapIfLittleEndian (*data)); } - inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint16) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue)))); } - inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint16) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue)))); } + inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + (double) maxValue)) * (int16) ByteOrder::swapIfBigEndian (*data)); } + inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + (double) maxValue)) * (int16) ByteOrder::swapIfLittleEndian (*data)); } + inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint16) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + (double) maxValue)))); } + inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint16) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + (double) maxValue)))); } inline int32 getAsInt32LE() const noexcept { return (int32) (ByteOrder::swapIfBigEndian ((uint16) *data) << 16); } inline int32 getAsInt32BE() const noexcept { return (int32) (ByteOrder::swapIfLittleEndian ((uint16) *data) << 16); } inline void setAsInt32LE (int32 newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint16) (newValue >> 16)); } @@ -177,10 +177,10 @@ public: inline void advance() noexcept { data += 3; } inline void skip (int numSamples) noexcept { data += 3 * numSamples; } - inline float getAsFloatLE() const noexcept { return (float) (ByteOrder::littleEndian24Bit (data) * (1.0 / (1.0 + maxValue))); } - inline float getAsFloatBE() const noexcept { return (float) (ByteOrder::bigEndian24Bit (data) * (1.0 / (1.0 + maxValue))); } - inline void setAsFloatLE (float newValue) noexcept { ByteOrder::littleEndian24BitToChars (jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue))), data); } - inline void setAsFloatBE (float newValue) noexcept { ByteOrder::bigEndian24BitToChars (jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue))), data); } + inline float getAsFloatLE() const noexcept { return (float) (ByteOrder::littleEndian24Bit (data) * (1.0 / (1.0 + (double) maxValue))); } + inline float getAsFloatBE() const noexcept { return (float) (ByteOrder::bigEndian24Bit (data) * (1.0 / (1.0 + (double) maxValue))); } + inline void setAsFloatLE (float newValue) noexcept { ByteOrder::littleEndian24BitToChars (jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + (double) maxValue))), data); } + inline void setAsFloatBE (float newValue) noexcept { ByteOrder::bigEndian24BitToChars (jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + (double) maxValue))), data); } inline int32 getAsInt32LE() const noexcept { return (int32) (((unsigned int) ByteOrder::littleEndian24Bit (data)) << 8); } inline int32 getAsInt32BE() const noexcept { return (int32) (((unsigned int) ByteOrder::bigEndian24Bit (data)) << 8); } inline void setAsInt32LE (int32 newValue) noexcept { ByteOrder::littleEndian24BitToChars (newValue >> 8, data); } @@ -202,10 +202,10 @@ public: inline void advance() noexcept { ++data; } inline void skip (int numSamples) noexcept { data += numSamples; } - inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfBigEndian (*data)); } - inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfLittleEndian (*data)); } - inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) (int32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); } - inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) (int32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); } + inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + (double) maxValue)) * (int32) ByteOrder::swapIfBigEndian (*data)); } + inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + (double) maxValue)) * (int32) ByteOrder::swapIfLittleEndian (*data)); } + inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) (int32) ((double) maxValue * jlimit (-1.0, 1.0, (double) newValue))); } + inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) (int32) ((double) maxValue * jlimit (-1.0, 1.0, (double) newValue))); } inline int32 getAsInt32LE() const noexcept { return (int32) ByteOrder::swapIfBigEndian (*data); } inline int32 getAsInt32BE() const noexcept { return (int32) ByteOrder::swapIfLittleEndian (*data); } inline void setAsInt32LE (int32 newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) newValue); } @@ -226,10 +226,10 @@ public: public: inline Int24in32 (void* d) noexcept : Int32 (d) {} - inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfBigEndian (*data)); } - inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfLittleEndian (*data)); } - inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); } - inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); } + inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + (double) maxValue)) * (int32) ByteOrder::swapIfBigEndian (*data)); } + inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + (double) maxValue)) * (int32) ByteOrder::swapIfLittleEndian (*data)); } + inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) ((double) maxValue * jlimit (-1.0, 1.0, (double) newValue))); } + inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) ((double) maxValue * jlimit (-1.0, 1.0, (double) newValue))); } inline int32 getAsInt32LE() const noexcept { return (int32) ByteOrder::swapIfBigEndian (*data) << 8; } inline int32 getAsInt32BE() const noexcept { return (int32) ByteOrder::swapIfLittleEndian (*data) << 8; } inline void setAsInt32LE (int32 newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) newValue >> 8); } @@ -261,8 +261,8 @@ public: #endif inline int32 getAsInt32LE() const noexcept { return (int32) roundToInt (jlimit (-1.0, 1.0, (double) getAsFloatLE()) * (double) maxValue); } inline int32 getAsInt32BE() const noexcept { return (int32) roundToInt (jlimit (-1.0, 1.0, (double) getAsFloatBE()) * (double) maxValue); } - inline void setAsInt32LE (int32 newValue) noexcept { setAsFloatLE ((float) (newValue * (1.0 / (1.0 + maxValue)))); } - inline void setAsInt32BE (int32 newValue) noexcept { setAsFloatBE ((float) (newValue * (1.0 / (1.0 + maxValue)))); } + inline void setAsInt32LE (int32 newValue) noexcept { setAsFloatLE ((float) (newValue * (1.0 / (1.0 + (double) maxValue)))); } + inline void setAsInt32BE (int32 newValue) noexcept { setAsFloatBE ((float) (newValue * (1.0 / (1.0 + (double) maxValue)))); } inline void clear() noexcept { *data = 0; } inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;} template inline void copyFromLE (SourceType& source) noexcept { setAsFloatLE (source.getAsFloat()); } @@ -528,8 +528,8 @@ public: if (v < mn) mn = v; } - return Range (mn * (float) (1.0 / (1.0 + Int32::maxValue)), - mx * (float) (1.0 / (1.0 + Int32::maxValue))); + return Range (mn * (float) (1.0 / (1.0 + (double) Int32::maxValue)), + mx * (float) (1.0 / (1.0 + (double) Int32::maxValue))); } /** Scans a block of data, returning the lowest and highest levels as floats */ diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.h b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.h index 53a3377..8cf5970 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.h +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.h @@ -31,6 +31,8 @@ namespace juce /** Maintains an ongoing measurement of the proportion of time which is being spent inside an audio callback. + + @tags{Audio} */ class JUCE_API AudioProcessLoadMeasurer { @@ -68,6 +70,8 @@ public: myCallback->doTheCallback(); } @endcode + + @tags{Audio} */ struct JUCE_API ScopedTimer { diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h index 9072ae9..f4ab1df 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h @@ -116,7 +116,7 @@ public: /** Copies another buffer. This buffer will make its own copy of the other's data, unless the buffer was created - using an external data buffer, in which case boths buffers will just point to the same + using an external data buffer, in which case both buffers will just point to the same shared block of data. */ AudioBuffer (const AudioBuffer& other) @@ -180,7 +180,7 @@ public: size (other.size), allocatedBytes (other.allocatedBytes), allocatedData (std::move (other.allocatedData)), - isClear (other.isClear) + isClear (other.isClear.load()) { if (numChannels < (int) numElementsInArray (preallocatedChannelSpace)) { @@ -206,7 +206,7 @@ public: size = other.size; allocatedBytes = other.allocatedBytes; allocatedData = std::move (other.allocatedData); - isClear = other.isClear; + isClear = other.isClear.load(); if (numChannels < (int) numElementsInArray (preallocatedChannelSpace)) { @@ -340,7 +340,7 @@ public: if (newNumSamples != size || newNumChannels != numChannels) { auto allocatedSamplesPerChannel = ((size_t) newNumSamples + 3) & ~3u; - auto channelListSize = ((sizeof (Type*) * (size_t) (newNumChannels + 1)) + 15) & ~15u; + auto channelListSize = ((static_cast (1 + newNumChannels) * sizeof (Type*)) + 15) & ~15u; auto newTotalBytes = ((size_t) newNumChannels * (size_t) allocatedSamplesPerChannel * sizeof (Type)) + channelListSize + 32; @@ -1071,12 +1071,21 @@ private: Type** channels; HeapBlock allocatedData; Type* preallocatedChannelSpace[32]; - bool isClear = false; + std::atomic isClear { false }; void allocateData() { + static_assert (std::alignment_of::value <= std::alignment_of::value, + "AudioBuffer cannot hold types with alignment requirements larger than that guaranteed by malloc"); jassert (size >= 0); - auto channelListSize = sizeof (Type*) * (size_t) (numChannels + 1); + + auto channelListSize = (size_t) (numChannels + 1) * sizeof (Type*); + auto requiredSampleAlignment = std::alignment_of::value; + size_t alignmentOverflow = channelListSize % requiredSampleAlignment; + + if (alignmentOverflow != 0) + channelListSize += requiredSampleAlignment - alignmentOverflow; + allocatedBytes = (size_t) numChannels * (size_t) size * sizeof (Type) + channelListSize + 32; allocatedData.malloc (allocatedBytes); channels = reinterpret_cast (allocatedData.get()); diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp index da99928..a01ad84 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp @@ -1139,6 +1139,7 @@ ScopedNoDenormals::~ScopedNoDenormals() noexcept #endif } + //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS @@ -1146,7 +1147,9 @@ ScopedNoDenormals::~ScopedNoDenormals() noexcept class FloatVectorOperationsTests : public UnitTest { public: - FloatVectorOperationsTests() : UnitTest ("FloatVectorOperations", "Audio") {} + FloatVectorOperationsTests() + : UnitTest ("FloatVectorOperations", UnitTestCategories::audio) + {} template struct TestRunner diff --git a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h index aab872b..93fd4c6 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h +++ b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h @@ -20,6 +20,7 @@ ============================================================================== */ + /******************************************************************************* The block below describes the properties of this module, and is read by the Projucer to automatically generate project code that uses it. @@ -29,17 +30,17 @@ BEGIN_JUCE_MODULE_DECLARATION - ID: juce_audio_basics - vendor: juce - version: 5.4.3 - name: JUCE audio and MIDI data classes - description: Classes for audio buffer manipulation, midi message handling, synthesis, etc. - website: http://www.juce.com/juce - license: ISC + ID: juce_audio_basics + vendor: juce + version: 5.4.7 + name: JUCE audio and MIDI data classes + description: Classes for audio buffer manipulation, midi message handling, synthesis, etc. + website: http://www.juce.com/juce + license: ISC - dependencies: juce_core - OSXFrameworks: Accelerate - iOSFrameworks: Accelerate + dependencies: juce_core + OSXFrameworks: Accelerate + iOSFrameworks: Accelerate END_JUCE_MODULE_DECLARATION diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp index 55e2597..0501698 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp @@ -25,58 +25,53 @@ namespace juce namespace MidiBufferHelpers { - inline int getEventTime (const void* const d) noexcept + inline int getEventTime (const void* d) noexcept { return readUnaligned (d); } - inline uint16 getEventDataSize (const void* const d) noexcept + inline uint16 getEventDataSize (const void* d) noexcept { return readUnaligned (static_cast (d) + sizeof (int32)); } - inline uint16 getEventTotalSize (const void* const d) noexcept + inline uint16 getEventTotalSize (const void* d) noexcept { return (uint16) (getEventDataSize (d) + sizeof (int32) + sizeof (uint16)); } - static int findActualEventLength (const uint8* const data, const int maxBytes) noexcept + static int findActualEventLength (const uint8* data, int maxBytes) noexcept { - unsigned int byte = (unsigned int) *data; - int size = 0; + auto byte = (unsigned int) *data; if (byte == 0xf0 || byte == 0xf7) { - const uint8* d = data + 1; + int i = 1; - while (d < data + maxBytes) - if (*d++ == 0xf7) + while (i < maxBytes) + if (data[i++] == 0xf7) break; - size = (int) (d - data); + return i; } - else if (byte == 0xff) + + if (byte == 0xff) { if (maxBytes == 1) - { - size = 1; - } - else - { - int n; - const int bytesLeft = MidiMessage::readVariableLengthVal (data + 1, n); - size = jmin (maxBytes, n + 2 + bytesLeft); - } - } - else if (byte >= 0x80) - { - size = jmin (maxBytes, MidiMessage::getMessageLengthFromFirstByte ((uint8) byte)); + return 1; + + int n; + auto bytesLeft = MidiMessage::readVariableLengthVal (data + 1, n); + return jmin (maxBytes, n + 2 + bytesLeft); } - return size; + if (byte >= 0x80) + return jmin (maxBytes, MidiMessage::getMessageLengthFromFirstByte ((uint8) byte)); + + return 0; } - static uint8* findEventAfter (uint8* d, uint8* endData, const int samplePosition) noexcept + static uint8* findEventAfter (uint8* d, uint8* endData, int samplePosition) noexcept { while (d < endData && getEventTime (d) <= samplePosition) d += getEventTotalSize (d); @@ -107,41 +102,41 @@ void MidiBuffer::clear() noexcept { data.clearQuick(); void MidiBuffer::ensureSize (size_t minimumNumBytes) { data.ensureStorageAllocated ((int) minimumNumBytes); } bool MidiBuffer::isEmpty() const noexcept { return data.size() == 0; } -void MidiBuffer::clear (const int startSample, const int numSamples) +void MidiBuffer::clear (int startSample, int numSamples) { - uint8* const start = MidiBufferHelpers::findEventAfter (data.begin(), data.end(), startSample - 1); - uint8* const end = MidiBufferHelpers::findEventAfter (start, data.end(), startSample + numSamples - 1); + auto start = MidiBufferHelpers::findEventAfter (data.begin(), data.end(), startSample - 1); + auto end = MidiBufferHelpers::findEventAfter (start, data.end(), startSample + numSamples - 1); data.removeRange ((int) (start - data.begin()), (int) (end - data.begin())); } -void MidiBuffer::addEvent (const MidiMessage& m, const int sampleNumber) +void MidiBuffer::addEvent (const MidiMessage& m, int sampleNumber) { addEvent (m.getRawData(), m.getRawDataSize(), sampleNumber); } -void MidiBuffer::addEvent (const void* const newData, const int maxBytes, const int sampleNumber) +void MidiBuffer::addEvent (const void* newData, int maxBytes, int sampleNumber) { - const int numBytes = MidiBufferHelpers::findActualEventLength (static_cast (newData), maxBytes); + auto numBytes = MidiBufferHelpers::findActualEventLength (static_cast (newData), maxBytes); if (numBytes > 0) { - const size_t newItemSize = (size_t) numBytes + sizeof (int32) + sizeof (uint16); - const int offset = (int) (MidiBufferHelpers::findEventAfter (data.begin(), data.end(), sampleNumber) - data.begin()); + auto newItemSize = (size_t) numBytes + sizeof (int32) + sizeof (uint16); + auto offset = (int) (MidiBufferHelpers::findEventAfter (data.begin(), data.end(), sampleNumber) - data.begin()); data.insertMultiple (offset, 0, (int) newItemSize); - uint8* const d = data.begin() + offset; + auto* d = data.begin() + offset; writeUnaligned (d, sampleNumber); - writeUnaligned (d + 4, static_cast (numBytes)); - memcpy (d + 6, newData, (size_t) numBytes); + d += sizeof (int32); + writeUnaligned (d, static_cast (numBytes)); + d += sizeof (uint16); + memcpy (d, newData, (size_t) numBytes); } } void MidiBuffer::addEvents (const MidiBuffer& otherBuffer, - const int startSample, - const int numSamples, - const int sampleDeltaToAdd) + int startSample, int numSamples, int sampleDeltaToAdd) { Iterator i (otherBuffer); i.setNextSamplePosition (startSample); @@ -159,9 +154,9 @@ void MidiBuffer::addEvents (const MidiBuffer& otherBuffer, int MidiBuffer::getNumEvents() const noexcept { int n = 0; - const uint8* const end = data.end(); + auto end = data.end(); - for (const uint8* d = data.begin(); d < end; ++n) + for (auto d = data.begin(); d < end; ++n) d += MidiBufferHelpers::getEventTotalSize (d); return n; @@ -177,11 +172,11 @@ int MidiBuffer::getLastEventTime() const noexcept if (data.size() == 0) return 0; - const uint8* const endData = data.end(); + auto endData = data.end(); - for (const uint8* d = data.begin();;) + for (auto d = data.begin();;) { - const uint8* const nextOne = d + MidiBufferHelpers::getEventTotalSize (d); + auto nextOne = d + MidiBufferHelpers::getEventTotalSize (d); if (nextOne >= endData) return MidiBufferHelpers::getEventTime (d); @@ -196,24 +191,24 @@ MidiBuffer::Iterator::Iterator (const MidiBuffer& b) noexcept { } -MidiBuffer::Iterator::~Iterator() noexcept{} +MidiBuffer::Iterator::~Iterator() noexcept {} -void MidiBuffer::Iterator::setNextSamplePosition (const int samplePosition) noexcept +void MidiBuffer::Iterator::setNextSamplePosition (int samplePosition) noexcept { data = buffer.data.begin(); - const uint8* const dataEnd = buffer.data.end(); + auto dataEnd = buffer.data.end(); while (data < dataEnd && MidiBufferHelpers::getEventTime (data) < samplePosition) data += MidiBufferHelpers::getEventTotalSize (data); } -bool MidiBuffer::Iterator::getNextEvent (const uint8* &midiData, int& numBytes, int& samplePosition) noexcept +bool MidiBuffer::Iterator::getNextEvent (const uint8*& midiData, int& numBytes, int& samplePosition) noexcept { if (data >= buffer.data.end()) return false; samplePosition = MidiBufferHelpers::getEventTime (data); - const int itemSize = MidiBufferHelpers::getEventDataSize (data); + auto itemSize = MidiBufferHelpers::getEventDataSize (data); numBytes = itemSize; midiData = data + sizeof (int32) + sizeof (uint16); data += sizeof (int32) + sizeof (uint16) + (size_t) itemSize; @@ -227,7 +222,7 @@ bool MidiBuffer::Iterator::getNextEvent (MidiMessage& result, int& samplePositio return false; samplePosition = MidiBufferHelpers::getEventTime (data); - const int itemSize = MidiBufferHelpers::getEventDataSize (data); + auto itemSize = MidiBufferHelpers::getEventDataSize (data); result = MidiMessage (data + sizeof (int32) + sizeof (uint16), itemSize, samplePosition); data += sizeof (int32) + sizeof (uint16) + (size_t) itemSize; diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.h b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.h index 27e86c7..8344a5e 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.h @@ -177,9 +177,6 @@ public: /** Creates a copy of an iterator. */ Iterator (const Iterator&) = default; - // VS2013 requires this, even if it's unused. - Iterator& operator= (const Iterator&) = delete; - /** Destructor. */ ~Iterator() noexcept; diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp index edad27d..58cbf7c 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp @@ -262,25 +262,26 @@ bool MidiFile::readFrom (InputStream& sourceStream, bool createMatchingNoteOffs) if (size > 16 && MidiFileHelpers::parseMidiHeader (d, timeFormat, fileType, expectedTracks)) { size -= (size_t) (d - static_cast (data.getData())); - int track = 0; - while (size > 0 && track < expectedTracks) + for (;;) { auto chunkType = (int) ByteOrder::bigEndianInt (d); d += 4; auto chunkSize = (int) ByteOrder::bigEndianInt (d); d += 4; - if (chunkSize <= 0) + if (chunkSize <= 0 || (size_t) chunkSize > size) break; if (chunkType == (int) ByteOrder::bigEndianInt ("MTrk")) readNextTrack (d, chunkSize, createMatchingNoteOffs); + if (++track >= expectedTracks) + break; + size -= (size_t) chunkSize + 8; d += chunkSize; - ++track; } return true; diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h index 5b20bc5..47aaf69 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h @@ -73,7 +73,7 @@ public: Represents a piano keyboard, keeping track of which keys are currently pressed. This object can parse a stream of midi events, using them to update its idea - of which keys are pressed for each individiual midi channel. + of which keys are pressed for each individual midi channel. When keys go up or down, it can broadcast these events to listener objects. @@ -135,7 +135,7 @@ public: It will also trigger a synchronous callback to the listeners to tell them that the key has gone up. - But if the note isn't acutally down for the given channel, this method will in fact do nothing. + But if the note isn't actually down for the given channel, this method will in fact do nothing. */ void noteOff (int midiChannel, int midiNoteNumber, float velocity); diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp index 6477e7d..d74aa58 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp @@ -87,8 +87,10 @@ MidiMessageSequence::MidiEventHolder* MidiMessageSequence::getEventPointer (int return list[index]; } -MidiMessageSequence::MidiEventHolder** MidiMessageSequence::begin() const noexcept { return list.begin(); } -MidiMessageSequence::MidiEventHolder** MidiMessageSequence::end() const noexcept { return list.end(); } +MidiMessageSequence::MidiEventHolder** MidiMessageSequence::begin() noexcept { return list.begin(); } +MidiMessageSequence::MidiEventHolder* const* MidiMessageSequence::begin() const noexcept { return list.begin(); } +MidiMessageSequence::MidiEventHolder** MidiMessageSequence::end() noexcept { return list.end(); } +MidiMessageSequence::MidiEventHolder* const* MidiMessageSequence::end() const noexcept { return list.end(); } double MidiMessageSequence::getTimeOfMatchingKeyUp (int index) const noexcept { @@ -344,11 +346,16 @@ void MidiMessageSequence::createControllerUpdatesForTime (int channelNumber, dou } } + +//============================================================================== +//============================================================================== #if JUCE_UNIT_TESTS -struct MidiMessageSequenceTest : public juce::UnitTest +struct MidiMessageSequenceTest : public UnitTest { - MidiMessageSequenceTest() : juce::UnitTest ("MidiMessageSequence") {} + MidiMessageSequenceTest() + : UnitTest ("MidiMessageSequence", UnitTestCategories::midi) + {} void runTest() override { @@ -371,7 +378,7 @@ struct MidiMessageSequenceTest : public juce::UnitTest expectEquals (s.getIndexOfMatchingKeyUp (0), 2); expectEquals (s.getIndexOfMatchingKeyUp (1), 3); - beginTest ("Time & indeces"); + beginTest ("Time & indices"); expectEquals (s.getNextIndexAtTime (0.5), 1); expectEquals (s.getNextIndexAtTime (2.5), 2); expectEquals (s.getNextIndexAtTime (9.0), 4); diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h index d4f4f92..5d4187e 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h @@ -103,10 +103,16 @@ public: MidiEventHolder* getEventPointer (int index) const noexcept; /** Iterator for the list of MidiEventHolders */ - MidiEventHolder** begin() const noexcept; + MidiEventHolder** begin() noexcept; /** Iterator for the list of MidiEventHolders */ - MidiEventHolder** end() const noexcept; + MidiEventHolder* const* begin() const noexcept; + + /** Iterator for the list of MidiEventHolders */ + MidiEventHolder** end() noexcept; + + /** Iterator for the list of MidiEventHolders */ + MidiEventHolder* const* end() const noexcept; /** Returns the time of the note-up that matches the note-on at this index. If the event at this index isn't a note-on, it'll just return 0. diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.cpp index 9e2c2df..10e8e03 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.cpp @@ -159,6 +159,7 @@ MidiBuffer MidiRPNGenerator::generate (int midiChannel, return buffer; } + //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS @@ -166,7 +167,9 @@ MidiBuffer MidiRPNGenerator::generate (int midiChannel, class MidiRPNDetectorTests : public UnitTest { public: - MidiRPNDetectorTests() : UnitTest ("MidiRPNDetector class", "MIDI/MPE") {} + MidiRPNDetectorTests() + : UnitTest ("MidiRPNDetector class", UnitTestCategories::midi) + {} void runTest() override { @@ -308,7 +311,9 @@ static MidiRPNDetectorTests MidiRPNDetectorUnitTests; class MidiRPNGeneratorTests : public UnitTest { public: - MidiRPNGeneratorTests() : UnitTest ("MidiRPNGenerator class", "MIDI/MPE") {} + MidiRPNGeneratorTests() + : UnitTest ("MidiRPNGenerator class", UnitTestCategories::midi) + {} void runTest() override { @@ -371,6 +376,6 @@ private: static MidiRPNGeneratorTests MidiRPNGeneratorUnitTests; -#endif // JUCE_UNIT_TESTS +#endif } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.h b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.h index 94ffc73..869afac 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.h @@ -142,7 +142,7 @@ public: @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). + 7-bit precision (a single MIDI byte). */ static MidiBuffer generate (int channel, int parameterNumber, diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp index a9a0075..df2e466 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp @@ -151,6 +151,7 @@ void MPEInstrument::processNextMidiEvent (const MidiMessage& message) else if (message.isPitchWheel()) processMidiPitchWheelMessage (message); else if (message.isChannelPressure()) processMidiChannelPressureMessage (message); else if (message.isController()) processMidiControllerMessage (message); + else if (message.isAftertouch()) processMidiAfterTouchMessage (message); } //============================================================================== @@ -241,7 +242,7 @@ void MPEInstrument::processMidiResetAllControllersMessage (const MidiMessage& me { auto& note = notes.getReference (i); - if (zone.isUsingChannelAsMemberChannel (note.midiChannel)) + if (zone.isUsing (note.midiChannel)) { note.keyState = MPENote::off; note.noteOffVelocity = MPEValue::from7BitInt (64); // some reasonable number @@ -252,6 +253,15 @@ void MPEInstrument::processMidiResetAllControllersMessage (const MidiMessage& me } } +void MPEInstrument::processMidiAfterTouchMessage (const MidiMessage& message) +{ + if (! isMasterChannel (message.getChannel())) + return; + + polyAftertouch (message.getChannel(), message.getNoteNumber(), + MPEValue::from7BitInt (message.getAfterTouchValue())); +} + //============================================================================== void MPEInstrument::handlePressureMSB (int midiChannel, int value) noexcept { @@ -284,7 +294,7 @@ void MPEInstrument::noteOn (int midiChannel, int midiNoteNumber, MPEValue midiNoteOnVelocity) { - if (! isMemberChannel (midiChannel)) + if (! isUsingChannel (midiChannel)) return; MPENote newNote (midiChannel, @@ -316,7 +326,7 @@ void MPEInstrument::noteOff (int midiChannel, int midiNoteNumber, MPEValue midiNoteOffVelocity) { - if (notes.isEmpty() || ! isMemberChannel (midiChannel)) + if (notes.isEmpty() || ! isUsingChannel (midiChannel)) return; const ScopedLock sl (lock); @@ -326,11 +336,13 @@ void MPEInstrument::noteOff (int midiChannel, note->keyState = (note->keyState == MPENote::keyDownAndSustained) ? MPENote::sustained : MPENote::off; note->noteOffVelocity = midiNoteOffVelocity; - // last dimension values received for this note should not be re-used for - // any new notes, so reset them: - pressureDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue::minValue(); - pitchbendDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue::centreValue(); - timbreDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue::centreValue(); + // If no more notes are playing on this channel, reset the dimension values + if (getLastNotePlayedPtr (midiChannel) == nullptr) + { + pressureDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue::minValue(); + pitchbendDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue::centreValue(); + timbreDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue::centreValue(); + } if (note->keyState == MPENote::off) { @@ -363,6 +375,24 @@ void MPEInstrument::timbre (int midiChannel, MPEValue value) updateDimension (midiChannel, timbreDimension, value); } +void MPEInstrument::polyAftertouch (int midiChannel, int midiNoteNumber, MPEValue value) +{ + const ScopedLock sl (lock); + + for (auto i = notes.size(); --i >= 0;) + { + auto& note = notes.getReference (i); + + if (note.midiChannel == midiChannel + && note.initialNote == midiNoteNumber + && pressureDimension.getValue (note) != value) + { + pressureDimension.getValue (note) = value; + callListenersDimensionChanged (note, pressureDimension); + } + } +} + MPEValue MPEInstrument::getInitialValueForNewNote (int midiChannel, MPEDimension& dimension) const { if (getLastNotePlayedPtr (midiChannel) != nullptr) @@ -416,7 +446,7 @@ void MPEInstrument::updateDimensionMaster (bool isLowerZone, MPEDimension& dimen { auto& note = notes.getReference (i); - if (! zone.isUsingChannelAsMemberChannel (note.midiChannel)) + if (! zone.isUsing (note.midiChannel)) continue; if (&dimension == &pitchbendDimension) @@ -467,9 +497,9 @@ void MPEInstrument::updateNoteTotalPitchbend (MPENote& note) { auto zone = zoneLayout.getLowerZone(); - if (! zone.isUsingChannelAsMemberChannel (note.midiChannel)) + if (! zone.isUsing (note.midiChannel)) { - if (zoneLayout.getUpperZone().isUsingChannelAsMemberChannel (note.midiChannel)) + if (zoneLayout.getUpperZone().isUsing (note.midiChannel)) { zone = zoneLayout.getUpperZone(); } @@ -481,7 +511,10 @@ void MPEInstrument::updateNoteTotalPitchbend (MPENote& note) } } - auto notePitchbendInSemitones = note.pitchbend.asSignedFloat() * zone.perNotePitchbendRange; + auto notePitchbendInSemitones = 0.0f; + + if (zone.isUsingChannelAsMemberChannel (note.midiChannel)) + notePitchbendInSemitones = note.pitchbend.asSignedFloat() * zone.perNotePitchbendRange; auto masterPitchbendInSemitones = pitchbendDimension.lastValueReceivedOnChannel[zone.getMasterChannel() - 1] .asSignedFloat() @@ -520,7 +553,7 @@ void MPEInstrument::handleSustainOrSostenuto (int midiChannel, bool isDown, bool { auto& note = notes.getReference (i); - if (legacyMode.isEnabled ? (note.midiChannel == midiChannel) : zone.isUsingChannelAsMemberChannel (note.midiChannel)) + if (legacyMode.isEnabled ? (note.midiChannel == midiChannel) : zone.isUsing (note.midiChannel)) { if (note.keyState == MPENote::keyDown && isDown) note.keyState = MPENote::keyDownAndSustained; @@ -560,7 +593,7 @@ void MPEInstrument::handleSustainOrSostenuto (int midiChannel, bool isDown, bool } //============================================================================== -bool MPEInstrument::isMemberChannel (int midiChannel) noexcept +bool MPEInstrument::isMemberChannel (int midiChannel) const noexcept { if (legacyMode.isEnabled) return legacyMode.channelRange.contains (midiChannel); @@ -574,8 +607,22 @@ bool MPEInstrument::isMasterChannel (int midiChannel) const noexcept if (legacyMode.isEnabled) return false; - return (midiChannel == 1 || midiChannel == 16); + const auto lowerZone = zoneLayout.getLowerZone(); + const auto upperZone = zoneLayout.getUpperZone(); + + return (lowerZone.isActive() && midiChannel == lowerZone.getMasterChannel()) + || (upperZone.isActive() && midiChannel == upperZone.getMasterChannel()); } + +bool MPEInstrument::isUsingChannel (int midiChannel) const noexcept +{ + if (legacyMode.isEnabled) + return legacyMode.channelRange.contains (midiChannel); + + return zoneLayout.getLowerZone().isUsing (midiChannel) + || zoneLayout.getUpperZone().isUsing (midiChannel); +} + //============================================================================== int MPEInstrument::getNumPlayingNotes() const noexcept { @@ -679,7 +726,7 @@ MPENote* MPEInstrument::getLastNotePlayedPtr (int midiChannel) noexcept const MPENote* MPEInstrument::getHighestNotePtr (int midiChannel) const noexcept { int initialNoteMax = -1; - MPENote* result = nullptr; + const MPENote* result = nullptr; for (auto i = notes.size(); --i >= 0;) { @@ -705,7 +752,7 @@ MPENote* MPEInstrument::getHighestNotePtr (int midiChannel) noexcept const MPENote* MPEInstrument::getLowestNotePtr (int midiChannel) const noexcept { int initialNoteMin = 128; - MPENote* result = nullptr; + const MPENote* result = nullptr; for (auto i = notes.size(); --i >= 0;) { @@ -744,6 +791,7 @@ void MPEInstrument::releaseAllNotes() notes.clear(); } + //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS @@ -752,7 +800,7 @@ class MPEInstrumentTests : public UnitTest { public: MPEInstrumentTests() - : UnitTest ("MPEInstrument class", "MIDI/MPE") + : UnitTest ("MPEInstrument class", UnitTestCategories::midi) { // using lower and upper MPE zones with the following layout for testing // @@ -798,12 +846,7 @@ public: UnitTestInstrument test; test.setZoneLayout (testLayout); - // note-on on master channel - ignore - test.noteOn (1, 60, MPEValue::from7BitInt (100)); - expectEquals (test.getNumPlayingNotes(), 0); - expectEquals (test.noteAddedCallCounter, 0); - - // note-on on any other channel - ignore + // note-on on unused channel - ignore test.noteOn (7, 60, MPEValue::from7BitInt (100)); expectEquals (test.getNumPlayingNotes(), 0); expectEquals (test.noteAddedCallCounter, 0); @@ -819,7 +862,21 @@ public: expectEquals (test.getNumPlayingNotes(), 0); expectEquals (test.noteReleasedCallCounter, 1); expectHasFinishedNote (test, 3, 60, 33); + + + // note-on on master channel - create new note + test.noteOn (1, 62, MPEValue::from7BitInt (100)); + expectEquals (test.getNumPlayingNotes(), 1); + expectEquals (test.noteAddedCallCounter, 2); + expectNote (test.getNote (1, 62), 100, 0, 8192, 64, MPENote::keyDown); + + // note-off + test.noteOff (1, 62, MPEValue::from7BitInt (33)); + expectEquals (test.getNumPlayingNotes(), 0); + expectEquals (test.noteReleasedCallCounter, 2); + expectHasFinishedNote (test, 1, 62, 33); } + { UnitTestInstrument test; test.setZoneLayout (testLayout); @@ -837,6 +894,7 @@ public: expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); expectEquals (test.noteReleasedCallCounter, 0); } + { // can have multiple notes on the same channel UnitTestInstrument test; @@ -1187,6 +1245,22 @@ public: expectNote (test.getNote (3, 60), 100, 78, 8192, 64, MPENote::keyDown); expectNote (test.getNote (3, 61), 100, 77, 8192, 64, MPENote::keyDown); } + + { + UnitTestInstrument test; + test.setZoneLayout (testLayout); + + // master channel will use poly-aftertouch for pressure + test.noteOn (16, 60, MPEValue::from7BitInt (100)); + expectNote (test.getNote (16, 60), 100, 0, 8192, 64, MPENote::keyDown); + test.aftertouch (16, 60, MPEValue::from7BitInt (27)); + expectNote (test.getNote (16, 60), 100, 27, 8192, 64, MPENote::keyDown); + + // member channels will not respond to poly-aftertouch + test.noteOn (3, 60, MPEValue::from7BitInt (100)); + test.aftertouch (3, 60, MPEValue::from7BitInt (50)); + expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); + } } beginTest ("pitchbend"); @@ -2142,6 +2216,12 @@ private: lastSostenutoPedalValueReceived = value; } + void aftertouch (int midiChannel, int midiNoteNumber, MPEValue value) + { + const auto message = juce::MidiMessage::aftertouchChange (midiChannel, midiNoteNumber, value.as7BitInt()); + processNextMidiEvent (message); + } + int noteOnCallCounter, noteOffCallCounter, pitchbendCallCounter, pressureCallCounter, timbreCallCounter, sustainPedalCallCounter, sostenutoPedalCallCounter, noteAddedCallCounter, @@ -2207,6 +2287,6 @@ private: static MPEInstrumentTests MPEInstrumentUnitTests; -#endif // JUCE_UNIT_TESTS +#endif } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.h b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.h index 436bb7b..91a0b2b 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.h +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.h @@ -27,7 +27,7 @@ namespace juce /** This class represents an instrument handling MPE. - It has an MPE zone layout and maintans a state of currently + It has an MPE zone layout and maintains a state of currently active (playing) notes and the values of their dimensions of expression. You can trigger and modulate notes: @@ -90,7 +90,7 @@ public: When in legacy mode, this will return true if the given channel is contained in the current legacy mode channel range; false otherwise. */ - bool isMemberChannel (int midiChannel) noexcept; + bool isMemberChannel (int midiChannel) const noexcept; /** Returns true if the given MIDI channel (1-16) is a master channel (channel 1 or 16). @@ -99,6 +99,14 @@ public: */ bool isMasterChannel (int midiChannel) const noexcept; + /** Returns true if the given MIDI channel (1-16) is used by 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 isUsingChannel (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 @@ -177,6 +185,13 @@ public: */ virtual void timbre (int midiChannel, MPEValue value); + /** Request a poly-aftertouch change for a given note number. + + The change will be broadcast to all notes sharing the channel and note + number of the change message. + */ + virtual void polyAftertouch (int midiChannel, int midiNoteNumber, MPEValue value); + /** Request a sustain pedal press or release. If midiChannel is a zone's master channel, this will act on all notes in @@ -246,12 +261,12 @@ public: /** Implement this callback to be informed whenever a new expressive MIDI note is triggered. */ - virtual void noteAdded (MPENote newNote) = 0; + virtual void noteAdded (MPENote newNote) { ignoreUnused (newNote); } /** Implement this callback to be informed whenever a currently playing MPE note's pressure value changes. */ - virtual void notePressureChanged (MPENote changedNote) = 0; + virtual void notePressureChanged (MPENote changedNote) { ignoreUnused (changedNote); } /** Implement this callback to be informed whenever a currently playing MPE note's pitchbend value changes. @@ -260,12 +275,12 @@ public: master channel pitchbend event, or if both occur simultaneously. Call MPENote::getFrequencyInHertz to get the effective note frequency. */ - virtual void notePitchbendChanged (MPENote changedNote) = 0; + virtual void notePitchbendChanged (MPENote changedNote) { ignoreUnused (changedNote); } /** Implement this callback to be informed whenever a currently playing MPE note's timbre value changes. */ - virtual void noteTimbreChanged (MPENote changedNote) = 0; + virtual void noteTimbreChanged (MPENote changedNote) { ignoreUnused (changedNote); } /** 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 @@ -274,14 +289,14 @@ public: Note: If the key state changes to MPENote::off, noteReleased is called instead. */ - virtual void noteKeyStateChanged (MPENote changedNote) = 0; + virtual void noteKeyStateChanged (MPENote changedNote) { ignoreUnused (changedNote); } /** 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; + virtual void noteReleased (MPENote finishedNote) { ignoreUnused (finishedNote); } }; //============================================================================== @@ -373,6 +388,7 @@ private: void processMidiChannelPressureMessage (const MidiMessage&); void processMidiControllerMessage (const MidiMessage&); void processMidiResetAllControllersMessage (const MidiMessage&); + void processMidiAfterTouchMessage (const MidiMessage&); void handlePressureMSB (int midiChannel, int value) noexcept; void handlePressureLSB (int midiChannel, int value) noexcept; void handleTimbreMSB (int midiChannel, int value) noexcept; diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp index 37c332f..214df7c 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp @@ -106,6 +106,7 @@ MidiBuffer MPEMessages::setZoneLayout (MPEZoneLayout layout) return buffer; } + //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS @@ -113,7 +114,9 @@ MidiBuffer MPEMessages::setZoneLayout (MPEZoneLayout layout) class MPEMessagesTests : public UnitTest { public: - MPEMessagesTests() : UnitTest ("MPEMessages class", "MIDI/MPE") {} + MPEMessagesTests() + : UnitTest ("MPEMessages class", UnitTestCategories::midi) + {} void runTest() override { @@ -233,6 +236,6 @@ private: static MPEMessagesTests MPEMessagesUnitTests; -#endif // JUCE_UNIT_TESTS +#endif } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEMessages.h b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEMessages.h index a6e31e8..1275dac 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEMessages.h +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEMessages.h @@ -99,7 +99,7 @@ public: /** 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 the current lower and upper + device to the layout passed in. This will first clear the current lower and upper zones, then then set the zones contained in the passed-in zone layout, and set their per-note and master pitchbend ranges to their current values. */ diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPENote.cpp b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPENote.cpp index c4c5707..742c778 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPENote.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPENote.cpp @@ -84,6 +84,7 @@ bool MPENote::operator!= (const MPENote& other) const noexcept return noteID != other.noteID; } + //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS @@ -91,7 +92,9 @@ bool MPENote::operator!= (const MPENote& other) const noexcept class MPENoteTests : public UnitTest { public: - MPENoteTests() : UnitTest ("MPENote class", "MIDI/MPE") {} + MPENoteTests() + : UnitTest ("MPENote class", UnitTestCategories::midi) + {} //============================================================================== void runTest() override @@ -119,6 +122,6 @@ private: static MPENoteTests MPENoteUnitTests; -#endif // JUCE_UNIT_TESTS +#endif } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp index fc76d40..8eaf95c 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp @@ -299,10 +299,14 @@ void MPESynthesiser::reduceNumVoices (const int newNumVoices) 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 (auto* voice : voices) - voice->noteStopped (allowTailOff); + { + const ScopedLock sl (voicesLock); + + // first turn off all voices (it's more efficient to do this immediately + // rather than to go through the MPEInstrument for this). + for (auto* voice : voices) + voice->noteStopped (allowTailOff); + } // finally make sure the MPE Instrument also doesn't have any notes anymore. instrument->releaseAllNotes(); @@ -311,6 +315,8 @@ void MPESynthesiser::turnOffAllVoices (bool allowTailOff) //============================================================================== void MPESynthesiser::renderNextSubBlock (AudioBuffer& buffer, int startSample, int numSamples) { + const ScopedLock sl (voicesLock); + for (auto* voice : voices) { if (voice->isActive()) @@ -320,6 +326,8 @@ void MPESynthesiser::renderNextSubBlock (AudioBuffer& buffer, int startSa void MPESynthesiser::renderNextSubBlock (AudioBuffer& buffer, int startSample, int numSamples) { + const ScopedLock sl (voicesLock); + for (auto* voice : voices) { if (voice->isActive()) diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h index 1dfae9d..8a999d6 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h @@ -69,7 +69,7 @@ public: @see MPESynthesiserBase, MPEInstrument */ - MPESynthesiser (MPEInstrument* instrument); + MPESynthesiser (MPEInstrument* instrumentToUse); /** Destructor. */ ~MPESynthesiser() override; @@ -247,7 +247,7 @@ protected: int numSamples) override; /** This will simply call renderNextBlock for each currently active - voice and fill the buffer with the sum. (souble-precision version) + voice and fill the buffer with the sum. (double-precision version) Override this method if you need to do more work to render your audio. */ void renderNextSubBlock (AudioBuffer& outputAudio, diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.cpp b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.cpp index 18efd66..8ebc185 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.cpp @@ -49,7 +49,7 @@ MPEChannelAssigner::MPEChannelAssigner (Range channelRange) int MPEChannelAssigner::findMidiChannelForNewNote (int noteNumber) noexcept { - if (numChannels == 1) + if (numChannels <= 1) return firstChannel; for (auto ch = firstChannel; (isLegacy || zone->isLowerZone() ? ch <= lastChannel : ch >= lastChannel); ch += channelIncrement) @@ -84,15 +84,29 @@ int MPEChannelAssigner::findMidiChannelForNewNote (int noteNumber) noexcept return midiChannelLastAssigned; } -void MPEChannelAssigner::noteOff (int noteNumber) +void MPEChannelAssigner::noteOff (int noteNumber, int midiChannel) { + const auto removeNote = [] (MidiChannel& ch, int noteNum) + { + if (ch.notes.removeAllInstancesOf (noteNum) > 0) + { + ch.lastNotePlayed = noteNum; + return true; + } + + return false; + }; + + if (midiChannel >= 0 && midiChannel < 17) + { + removeNote (midiChannels[midiChannel], noteNumber); + return; + } + for (auto& ch : midiChannels) { - if (ch.notes.removeAllInstancesOf (noteNumber) > 0) - { - ch.lastNotePlayed = noteNumber; + if (removeNote (ch, noteNumber)) return; - } } } @@ -255,6 +269,7 @@ void MPEChannelRemapper::zeroArrays() } } + //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS @@ -262,7 +277,7 @@ void MPEChannelRemapper::zeroArrays() struct MPEUtilsUnitTests : public UnitTest { MPEUtilsUnitTests() - : UnitTest ("MPE Utilities", "MIDI/MPE") + : UnitTest ("MPE Utilities", UnitTestCategories::midi) {} void runTest() override @@ -475,4 +490,5 @@ struct MPEUtilsUnitTests : public UnitTest static MPEUtilsUnitTests MPEUtilsUnitTests; #endif + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.h b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.h index 7cbfd2b..3f5f7dd 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.h +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.h @@ -65,8 +65,11 @@ public: /** You must call this method for all note-offs that you receive so that this class can keep track of the currently playing notes internally. + + You can specify the channel number the note off happened on. If you don't, it will + look through all channels to find the registered midi note matching the given note number. */ - void noteOff (int noteNumber); + void noteOff (int noteNumber, int midiChannel = -1); /** Call this to clear all currently playing notes. */ void allNotesOff(); diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEValue.cpp b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEValue.cpp index b9a0713..ccc20ab 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEValue.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEValue.cpp @@ -82,6 +82,7 @@ bool MPEValue::operator!= (const MPEValue& other) const noexcept return ! operator== (other); } + //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS @@ -89,7 +90,9 @@ bool MPEValue::operator!= (const MPEValue& other) const noexcept class MPEValueTests : public UnitTest { public: - MPEValueTests() : UnitTest ("MPEValue class", "MIDI/MPE") {} + MPEValueTests() + : UnitTest ("MPEValue class", UnitTestCategories::midi) + {} void runTest() override { @@ -165,6 +168,6 @@ private: static MPEValueTests MPEValueUnitTests; -#endif // JUCE_UNIT_TESTS +#endif } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp index 7f94dee..dfcba78 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp @@ -205,6 +205,7 @@ void MPEZoneLayout::checkAndLimitZoneParameters (int minValue, int maxValue, } } + //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS @@ -212,7 +213,9 @@ void MPEZoneLayout::checkAndLimitZoneParameters (int minValue, int maxValue, class MPEZoneLayoutTests : public UnitTest { public: - MPEZoneLayoutTests() : UnitTest ("MPEZoneLayout class", "MIDI/MPE") {} + MPEZoneLayoutTests() + : UnitTest ("MPEZoneLayout class", UnitTestCategories::midi) + {} void runTest() override { @@ -382,6 +385,6 @@ public: static MPEZoneLayoutTests MPEZoneLayoutUnitTests; -#endif // JUCE_UNIT_TESTS +#endif } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h index 955deb3..58523ee 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h @@ -98,6 +98,11 @@ public: : (channel < 16 && channel >= 16 - numMemberChannels); } + bool isUsing (int channel) const noexcept + { + return isUsingChannelAsMemberChannel (channel) || channel == getMasterChannel(); + } + bool operator== (const Zone& other) const noexcept { return lowerZone == other.lowerZone && numMemberChannels == other.numMemberChannels && perNotePitchbendRange == other.perNotePitchbendRange diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_AudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_AudioSource.h index 3b48ee0..401c4f7 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_AudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_AudioSource.h @@ -122,7 +122,7 @@ public: An AudioSource has two states: prepared and unprepared. - The prepareToPlay() method is guaranteed to be called at least once on an 'unpreprared' + The prepareToPlay() method is guaranteed to be called at least once on an 'unprepared' source to put it into a 'prepared' state before any calls will be made to getNextAudioBlock(). This callback allows the source to initialise any resources it might need when playing. diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.cpp index 3cec221..d5263ac 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.cpp @@ -145,9 +145,9 @@ void ChannelRemappingAudioSource::getNextAudioBlock (const AudioSourceChannelInf } //============================================================================== -XmlElement* ChannelRemappingAudioSource::createXml() const +std::unique_ptr ChannelRemappingAudioSource::createXml() const { - XmlElement* e = new XmlElement ("MAPPINGS"); + auto e = std::make_unique ("MAPPINGS"); String ins, outs; const ScopedLock sl (lock); diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.h index 69761fa..928a20c 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.h @@ -112,7 +112,7 @@ public: /** Returns an XML object to encapsulate the state of the mappings. @see restoreFromXml */ - XmlElement* createXml() const; + std::unique_ptr createXml() const; /** Restores the mappings from an XML object created by createXML(). @see createXml diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp index 1377071..d123e4c 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp @@ -27,11 +27,6 @@ ResamplingAudioSource::ResamplingAudioSource (AudioSource* const inputSource, const bool deleteInputWhenDeleted, const int channels) : input (inputSource, deleteInputWhenDeleted), - ratio (1.0), - lastRatio (1.0), - bufferPos (0), - sampsInBuffer (0), - subSampleOffset (0), numChannels (channels) { jassert (input != nullptr); @@ -67,6 +62,8 @@ void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected, double s void ResamplingAudioSource::flushBuffers() { + const ScopedLock sl (callbackLock); + buffer.clear(); bufferPos = 0; sampsInBuffer = 0; @@ -82,10 +79,12 @@ void ResamplingAudioSource::releaseResources() void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) { + const ScopedLock sl (callbackLock); + double localRatio; { - const SpinLock::ScopedLockType sl (ratioLock); + const SpinLock::ScopedLockType ratioSl (ratioLock); localRatio = ratio; } diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h index e33396b..49b4974 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h @@ -76,12 +76,13 @@ public: private: //============================================================================== OptionalScopedPointer input; - double ratio, lastRatio; + double ratio = 1.0, lastRatio = 1.0; AudioBuffer buffer; - int bufferPos, sampsInBuffer; - double subSampleOffset; + int bufferPos = 0, sampsInBuffer = 0; + double subSampleOffset = 0.0; double coefficients[6]; SpinLock ratioLock; + CriticalSection callbackLock; const int numChannels; HeapBlock destBuffers; HeapBlock srcBuffers; diff --git a/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h b/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h index cdff377..7c39c81 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h +++ b/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h @@ -274,11 +274,6 @@ private: AudioBuffer tempBuffer; - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // Note the new parameters for this method. - virtual int stopNote (bool) { return 0; } - #endif - JUCE_LEAK_DETECTOR (SynthesiserVoice) }; diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_ADSR.h b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_ADSR.h index 481782e..8f50cee 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_ADSR.h +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_ADSR.h @@ -30,6 +30,8 @@ namespace juce To use it, call setSampleRate() with the current sample rate and give it some parameters with setParameters() then call getNextSample() to get the envelope value to be applied to each audio sample or applyEnvelopeToBuffer() to apply the envelope to a whole buffer. + + @tags{Audio} */ class ADSR { @@ -42,7 +44,11 @@ public: } //============================================================================== - /** Holds the parameters being used by an ADSR object. */ + /** + Holds the parameters being used by an ADSR object. + + @tags{Audio} + */ struct Parameters { /** Attack time in seconds. */ @@ -107,9 +113,19 @@ public: /** Starts the attack phase of the envelope. */ void noteOn() { - if (attackRate > 0.0f) currentState = State::attack; - else if (decayRate > 0.0f) currentState = State::decay; - else currentState = State::sustain; + if (attackRate > 0.0f) + { + currentState = State::attack; + } + else if (decayRate > 0.0f) + { + envelopeVal = 1.0f; + currentState = State::decay; + } + else + { + currentState = State::sustain; + } } /** Starts the release phase of the envelope. */ @@ -117,11 +133,9 @@ public: { if (currentState != State::idle) { - if (releaseRate > 0.0f) + if (currentParameters.release > 0.0f) { - if (currentState != State::sustain) - releaseRate = static_cast (envelopeVal / (currentParameters.release * sr)); - + releaseRate = static_cast (envelopeVal / (currentParameters.release * sr)); currentState = State::release; } else @@ -212,7 +226,6 @@ private: attackRate = (parameters.attack > 0.0f ? static_cast (1.0f / (parameters.attack * sr)) : -1.0f); decayRate = (parameters.decay > 0.0f ? static_cast ((1.0f - sustainLevel) / (parameters.decay * sr)) : -1.0f); - releaseRate = (parameters.release > 0.0f ? static_cast (sustainLevel / (parameters.release * sr)) : -1.0f); } void checkCurrentState() @@ -229,11 +242,7 @@ private: Parameters currentParameters; double sr = 0.0; - - float envelopeVal = 0.0f; - - float sustainLevel = 0.0f; - float attackRate = 0.0f, decayRate = 0.0f, releaseRate = 0.0f; + float envelopeVal = 0.0f, sustainLevel = 0.0f, attackRate = 0.0f, decayRate = 0.0f, releaseRate = 0.0f; }; } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_CatmullRomInterpolator.h b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_CatmullRomInterpolator.h index ab3ca75..2d89a34 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_CatmullRomInterpolator.h +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_CatmullRomInterpolator.h @@ -42,6 +42,9 @@ public: CatmullRomInterpolator() noexcept; ~CatmullRomInterpolator() noexcept; + CatmullRomInterpolator (CatmullRomInterpolator&&) noexcept = default; + CatmullRomInterpolator& operator= (CatmullRomInterpolator&&) noexcept = default; + /** Resets the state of the interpolator. Call this when there's a break in the continuity of the input data stream. */ diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_LagrangeInterpolator.h b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_LagrangeInterpolator.h index 6914e46..25598e7 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_LagrangeInterpolator.h +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_LagrangeInterpolator.h @@ -42,6 +42,9 @@ public: LagrangeInterpolator() noexcept; ~LagrangeInterpolator() noexcept; + LagrangeInterpolator (LagrangeInterpolator&&) noexcept = default; + LagrangeInterpolator& operator= (LagrangeInterpolator&&) noexcept = default; + /** Resets the state of the interpolator. Call this when there's a break in the continuity of the input data stream. */ diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.cpp b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.cpp index 8a6d0b7..643dfd1 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.cpp @@ -32,7 +32,7 @@ class SmoothedValueTests : public UnitTest { public: SmoothedValueTests() - : UnitTest ("SmoothedValueTests", "SmoothedValues") + : UnitTest ("SmoothedValueTests", UnitTestCategories::smoothedValues) {} void runTest() override diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.h b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.h index 047434e..a472fb0 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.h +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.h @@ -179,10 +179,18 @@ protected: */ namespace ValueSmoothingTypes { - /** Used to indicate a linear smoothing between values. */ + /** + Used to indicate a linear smoothing between values. + + @tags{Audio} + */ struct Linear {}; - /** Used to indicate a smoothing between multiplicative values. */ + /** + Used to indicate a smoothing between multiplicative values. + + @tags{Audio} + */ struct Multiplicative {}; } @@ -408,7 +416,7 @@ class CommonSmoothedValueTests : public UnitTest { public: CommonSmoothedValueTests() - : UnitTest ("CommonSmoothedValueTests", "SmoothedValues") + : UnitTest ("CommonSmoothedValueTests", UnitTestCategories::smoothedValues) {} void runTest() override diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp index f81d369..7c8625c 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp @@ -105,7 +105,7 @@ void AudioDeviceManager::createDeviceTypesIfNeeded() createAudioDeviceTypes (types); for (auto* t : types) - addAudioDeviceType (t); + addAudioDeviceType (std::unique_ptr (t)); types.clear (false); @@ -139,12 +139,10 @@ void AudioDeviceManager::audioDeviceListChanged() { closeAudioDevice(); - std::unique_ptr e (createStateXml()); - - if (e == nullptr) - initialiseDefault (preferredDeviceName, ¤tSetup); - else + if (auto e = createStateXml()) initialiseFromXML (*e, true, preferredDeviceName, ¤tSetup); + else + initialiseDefault (preferredDeviceName, ¤tSetup); } if (currentAudioDevice != nullptr) @@ -174,23 +172,40 @@ void AudioDeviceManager::createAudioDeviceTypes (OwnedArray& addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_ASIO()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_CoreAudio()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_iOSAudio()); + addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_Bela()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_ALSA()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_JACK()); - addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_Bela()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_Oboe()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_OpenSLES()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_Android()); } -void AudioDeviceManager::addAudioDeviceType (AudioIODeviceType* newDeviceType) +void AudioDeviceManager::addAudioDeviceType (std::unique_ptr newDeviceType) { if (newDeviceType != nullptr) { jassert (lastDeviceTypeConfigs.size() == availableDeviceTypes.size()); - availableDeviceTypes.add (newDeviceType); + + availableDeviceTypes.add (newDeviceType.release()); lastDeviceTypeConfigs.add (new AudioDeviceSetup()); - newDeviceType->addListener (callbackHandler.get()); + availableDeviceTypes.getLast()->addListener (callbackHandler.get()); + } +} + +void AudioDeviceManager::removeAudioDeviceType (AudioIODeviceType* deviceTypeToRemove) +{ + if (deviceTypeToRemove != nullptr) + { + jassert (lastDeviceTypeConfigs.size() == availableDeviceTypes.size()); + + auto index = availableDeviceTypes.indexOf (deviceTypeToRemove); + + if (auto removed = std::unique_ptr (availableDeviceTypes.removeAndReturn (index))) + { + removed->removeListener (callbackHandler.get()); + lastDeviceTypeConfigs.remove (index, true); + } } } @@ -306,19 +321,66 @@ String AudioDeviceManager::initialiseFromXML (const XmlElement& xml, error = setAudioDeviceSetup (setup, true); - midiInsFromXml.clear(); + if (error.isNotEmpty() && selectDefaultDeviceOnFailure) + error = initialise (numInputChansNeeded, numOutputChansNeeded, nullptr, false, preferredDefaultDeviceName); + + midiDeviceInfosFromXml.clear(); + enabledMidiInputs.clear(); forEachXmlChildElementWithTagName (xml, c, "MIDIINPUT") - midiInsFromXml.add (c->getStringAttribute ("name")); + midiDeviceInfosFromXml.add ({ c->getStringAttribute ("name"), c->getStringAttribute ("identifier") }); - for (auto& m : MidiInput::getDevices()) - setMidiInputEnabled (m, midiInsFromXml.contains (m)); + auto isIdentifierAvailable = [] (const Array& available, const String& identifier) + { + for (auto& device : available) + if (device.identifier == identifier) + return true; - if (error.isNotEmpty() && selectDefaultDeviceOnFailure) - error = initialise (numInputChansNeeded, numOutputChansNeeded, - nullptr, false, preferredDefaultDeviceName); + return false; + }; - setDefaultMidiOutput (xml.getStringAttribute ("defaultMidiOutput")); + auto getUpdatedIdentifierForName = [&] (const Array& available, const String& name) -> String + { + for (auto& device : available) + if (device.name == name) + return device.identifier; + + return {}; + }; + + auto inputs = MidiInput::getAvailableDevices(); + + for (auto& info : midiDeviceInfosFromXml) + { + if (isIdentifierAvailable (inputs, info.identifier)) + { + setMidiInputDeviceEnabled (info.identifier, true); + } + else + { + auto identifier = getUpdatedIdentifierForName (inputs, info.name); + + if (identifier.isNotEmpty()) + setMidiInputDeviceEnabled (identifier, true); + } + } + + MidiDeviceInfo defaultOutputDeviceInfo (xml.getStringAttribute ("defaultMidiOutput"), + xml.getStringAttribute ("defaultMidiOutputDevice")); + + auto outputs = MidiOutput::getAvailableDevices(); + + if (isIdentifierAvailable (outputs, defaultOutputDeviceInfo.identifier)) + { + setDefaultMidiOutputDevice (defaultOutputDeviceInfo.identifier); + } + else + { + auto identifier = getUpdatedIdentifierForName (outputs, defaultOutputDeviceInfo.name); + + if (identifier.isNotEmpty()) + setDefaultMidiOutputDevice (identifier); + } return error; } @@ -344,9 +406,12 @@ void AudioDeviceManager::insertDefaultDeviceNames (AudioDeviceSetup& setup) cons } } -XmlElement* AudioDeviceManager::createStateXml() const +std::unique_ptr AudioDeviceManager::createStateXml() const { - return createCopyIfNotNull (lastExplicitSettings.get()); + if (lastExplicitSettings != nullptr) + return std::make_unique (*lastExplicitSettings); + + return {}; } //============================================================================== @@ -439,28 +504,37 @@ AudioIODeviceType* AudioDeviceManager::getCurrentDeviceTypeObject() const return availableDeviceTypes.getFirst(); } +static void updateSetupChannels (AudioDeviceManager::AudioDeviceSetup& setup, int defaultNumIns, int defaultNumOuts) +{ + auto updateChannels = [](const String& deviceName, BigInteger& channels, int defaultNumChannels) + { + if (deviceName.isEmpty()) + { + channels.clear(); + } + else if (defaultNumChannels != -1) + { + channels.clear(); + channels.setRange (0, defaultNumChannels, true); + } + }; + + updateChannels (setup.inputDeviceName, setup.inputChannels, setup.useDefaultInputChannels ? defaultNumIns : -1); + updateChannels (setup.outputDeviceName, setup.outputChannels, setup.useDefaultOutputChannels ? defaultNumOuts : -1); +} + String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup, bool treatAsChosenDevice) { jassert (&newSetup != ¤tSetup); // this will have no effect - if (newSetup == currentSetup && currentAudioDevice != nullptr) + if (newSetup != currentSetup) + sendChangeMessage(); + else if (currentAudioDevice != nullptr) return {}; - if (! (newSetup == currentSetup)) - sendChangeMessage(); - - stopDevice(); - - if (! newSetup.useDefaultInputChannels) - numInputChansNeeded = newSetup.inputChannels.countNumberOfSetBits(); - - if (! newSetup.useDefaultOutputChannels) - numOutputChansNeeded = newSetup.outputChannels.countNumberOfSetBits(); - - auto* type = getCurrentDeviceTypeObject(); - - if (type == nullptr) + if (getCurrentDeviceTypeObject() == nullptr + || (newSetup.inputDeviceName.isEmpty() && newSetup.outputDeviceName.isEmpty())) { deleteCurrentDevice(); @@ -470,6 +544,8 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup return {}; } + stopDevice(); + String error; if (currentSetup.inputDeviceName != newSetup.inputDeviceName @@ -479,6 +555,8 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup deleteCurrentDevice(); scanDevicesIfNeeded(); + auto* type = getCurrentDeviceTypeObject(); + if (newSetup.outputDeviceName.isNotEmpty() && ! deviceListContains (type, false, newSetup.outputDeviceName)) return "No such device: " + newSetup.outputDeviceName; @@ -499,32 +577,16 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup deleteCurrentDevice(); return error; } - - if (newSetup.useDefaultInputChannels) - { - inputChannels.clear(); - inputChannels.setRange (0, numInputChansNeeded, true); - } - - if (newSetup.useDefaultOutputChannels) - { - outputChannels.clear(); - outputChannels.setRange (0, numOutputChansNeeded, true); - } - - if (newSetup.inputDeviceName.isEmpty()) inputChannels.clear(); - if (newSetup.outputDeviceName.isEmpty()) outputChannels.clear(); } - if (! newSetup.useDefaultInputChannels) - inputChannels = newSetup.inputChannels; - - if (! newSetup.useDefaultOutputChannels) - outputChannels = newSetup.outputChannels; - currentSetup = newSetup; - if (inputChannels.isZero() && outputChannels.isZero()) + if (! currentSetup.useDefaultInputChannels) numInputChansNeeded = currentSetup.inputChannels.countNumberOfSetBits(); + if (! currentSetup.useDefaultOutputChannels) numOutputChansNeeded = currentSetup.outputChannels.countNumberOfSetBits(); + + updateSetupChannels (currentSetup, numInputChansNeeded, numOutputChansNeeded); + + if (currentSetup.inputChannels.isZero() && currentSetup.outputChannels.isZero()) { if (treatAsChosenDevice) updateXml(); @@ -532,11 +594,11 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup return {}; } - currentSetup.sampleRate = chooseBestSampleRate (newSetup.sampleRate); - currentSetup.bufferSize = chooseBestBufferSize (newSetup.bufferSize); + currentSetup.sampleRate = chooseBestSampleRate (currentSetup.sampleRate); + currentSetup.bufferSize = chooseBestBufferSize (currentSetup.bufferSize); - error = currentAudioDevice->open (inputChannels, - outputChannels, + error = currentAudioDevice->open (currentSetup.inputChannels, + currentSetup.outputChannels, currentSetup.sampleRate, currentSetup.bufferSize); @@ -662,24 +724,37 @@ void AudioDeviceManager::updateXml() lastExplicitSettings->setAttribute ("audioDeviceOutChans", currentSetup.outputChannels.toString (2)); } - for (int i = 0; i < enabledMidiInputs.size(); ++i) - lastExplicitSettings->createNewChildElement ("MIDIINPUT") - ->setAttribute ("name", enabledMidiInputs[i]->getName()); + for (auto& input : enabledMidiInputs) + { + auto* child = lastExplicitSettings->createNewChildElement ("MIDIINPUT"); - if (midiInsFromXml.size() > 0) + child->setAttribute ("name", input->getName()); + child->setAttribute ("identifier", input->getIdentifier()); + } + + if (midiDeviceInfosFromXml.size() > 0) { // Add any midi devices that have been enabled before, but which aren't currently // open because the device has been disconnected. - const StringArray availableMidiDevices (MidiInput::getDevices()); + auto availableMidiDevices = MidiInput::getAvailableDevices(); - for (int i = 0; i < midiInsFromXml.size(); ++i) - if (! availableMidiDevices.contains (midiInsFromXml[i], true)) - lastExplicitSettings->createNewChildElement ("MIDIINPUT") - ->setAttribute ("name", midiInsFromXml[i]); + for (auto& d : midiDeviceInfosFromXml) + { + if (! availableMidiDevices.contains (d)) + { + auto* child = lastExplicitSettings->createNewChildElement ("MIDIINPUT"); + + child->setAttribute ("name", d.name); + child->setAttribute ("identifier", d.identifier); + } + } } - if (defaultMidiOutputName.isNotEmpty()) - lastExplicitSettings->setAttribute ("defaultMidiOutput", defaultMidiOutputName); + if (defaultMidiOutputDeviceInfo != MidiDeviceInfo()) + { + lastExplicitSettings->setAttribute ("defaultMidiOutput", defaultMidiOutputDeviceInfo.name); + lastExplicitSettings->setAttribute ("defaultMidiOutputDevice", defaultMidiOutputDeviceInfo.identifier); + } } //============================================================================== @@ -756,7 +831,7 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat else { for (int i = 0; i < numOutputChannels; ++i) - zeromem (outputChannelData[i], sizeof (float) * (size_t) numSamples); + zeromem (outputChannelData[i], (size_t) numSamples * sizeof (float)); } if (testSound != nullptr) @@ -816,28 +891,23 @@ double AudioDeviceManager::getCpuUsage() const } //============================================================================== -void AudioDeviceManager::setMidiInputEnabled (const String& name, const bool enabled) +void AudioDeviceManager::setMidiInputDeviceEnabled (const String& identifier, bool enabled) { - if (enabled != isMidiInputEnabled (name)) + if (enabled != isMidiInputDeviceEnabled (identifier)) { if (enabled) { - auto index = MidiInput::getDevices().indexOf (name); - - if (index >= 0) + if (auto midiIn = MidiInput::openDevice (identifier, callbackHandler.get())) { - if (auto* midiIn = MidiInput::openDevice (index, callbackHandler.get())) - { - enabledMidiInputs.add (midiIn); - midiIn->start(); - } + enabledMidiInputs.push_back (std::move (midiIn)); + enabledMidiInputs.back()->start(); } } else { - for (int i = enabledMidiInputs.size(); --i >= 0;) - if (enabledMidiInputs[i]->getName() == name) - enabledMidiInputs.remove (i); + auto removePredicate = [identifier] (const std::unique_ptr& in) { return in->getIdentifier() == identifier; }; + enabledMidiInputs.erase (std::remove_if (std::begin (enabledMidiInputs), std::end (enabledMidiInputs), removePredicate), + std::end (enabledMidiInputs)); } updateXml(); @@ -845,37 +915,33 @@ void AudioDeviceManager::setMidiInputEnabled (const String& name, const bool ena } } -bool AudioDeviceManager::isMidiInputEnabled (const String& name) const +bool AudioDeviceManager::isMidiInputDeviceEnabled (const String& identifier) const { - for (auto* mi : enabledMidiInputs) - if (mi->getName() == name) + for (auto& mi : enabledMidiInputs) + if (mi->getIdentifier() == identifier) return true; return false; } -void AudioDeviceManager::addMidiInputCallback (const String& name, MidiInputCallback* callbackToAdd) +void AudioDeviceManager::addMidiInputDeviceCallback (const String& identifier, MidiInputCallback* callbackToAdd) { - removeMidiInputCallback (name, callbackToAdd); + removeMidiInputDeviceCallback (identifier, callbackToAdd); - if (name.isEmpty() || isMidiInputEnabled (name)) + if (identifier.isEmpty() || isMidiInputDeviceEnabled (identifier)) { const ScopedLock sl (midiCallbackLock); - - MidiCallbackInfo mc; - mc.deviceName = name; - mc.callback = callbackToAdd; - midiCallbacks.add (mc); + midiCallbacks.add ({ identifier, callbackToAdd }); } } -void AudioDeviceManager::removeMidiInputCallback (const String& name, MidiInputCallback* callbackToRemove) +void AudioDeviceManager::removeMidiInputDeviceCallback (const String& identifier, MidiInputCallback* callbackToRemove) { for (int i = midiCallbacks.size(); --i >= 0;) { - auto& mc = midiCallbacks.getReference(i); + auto& mc = midiCallbacks.getReference (i); - if (mc.callback == callbackToRemove && mc.deviceName == name) + if (mc.callback == callbackToRemove && mc.deviceIdentifier == identifier) { const ScopedLock sl (midiCallbackLock); midiCallbacks.remove (i); @@ -890,15 +956,15 @@ void AudioDeviceManager::handleIncomingMidiMessageInt (MidiInput* source, const const ScopedLock sl (midiCallbackLock); for (auto& mc : midiCallbacks) - if (mc.deviceName.isEmpty() || mc.deviceName == source->getName()) + if (mc.deviceIdentifier.isEmpty() || mc.deviceIdentifier == source->getIdentifier()) mc.callback->handleIncomingMidiMessage (source, message); } } //============================================================================== -void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName) +void AudioDeviceManager::setDefaultMidiOutputDevice (const String& identifier) { - if (defaultMidiOutputName != deviceName) + if (defaultMidiOutputDeviceInfo.identifier != identifier) { Array oldCallbacks; @@ -909,13 +975,17 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName) if (currentAudioDevice != nullptr) for (int i = oldCallbacks.size(); --i >= 0;) - oldCallbacks.getUnchecked(i)->audioDeviceStopped(); + oldCallbacks.getUnchecked (i)->audioDeviceStopped(); defaultMidiOutput.reset(); - defaultMidiOutputName = deviceName; - if (deviceName.isNotEmpty()) - defaultMidiOutput.reset (MidiOutput::openDevice (MidiOutput::getDevices().indexOf (deviceName))); + if (identifier.isNotEmpty()) + defaultMidiOutput = MidiOutput::openDevice (identifier); + + if (defaultMidiOutput != nullptr) + defaultMidiOutputDeviceInfo = defaultMidiOutput->getDeviceInfo(); + else + defaultMidiOutputDeviceInfo = {}; if (currentAudioDevice != nullptr) for (auto* c : oldCallbacks) @@ -1020,4 +1090,70 @@ int AudioDeviceManager::getXRunCount() const noexcept return jmax (0, deviceXRuns) + loadMeasurer.getXRunCount(); } +//============================================================================== +// Deprecated +void AudioDeviceManager::setMidiInputEnabled (const String& name, const bool enabled) +{ + for (auto& device : MidiInput::getAvailableDevices()) + { + if (device.name == name) + { + setMidiInputDeviceEnabled (device.identifier, enabled); + return; + } + } +} + +bool AudioDeviceManager::isMidiInputEnabled (const String& name) const +{ + for (auto& device : MidiInput::getAvailableDevices()) + if (device.name == name) + return isMidiInputDeviceEnabled (device.identifier); + + return false; +} + +void AudioDeviceManager::addMidiInputCallback (const String& name, MidiInputCallback* callbackToAdd) +{ + if (name.isEmpty()) + { + addMidiInputDeviceCallback ({}, callbackToAdd); + } + else + { + for (auto& device : MidiInput::getAvailableDevices()) + { + if (device.name == name) + { + addMidiInputDeviceCallback (device.identifier, callbackToAdd); + return; + } + } + } +} + +void AudioDeviceManager::removeMidiInputCallback (const String& name, MidiInputCallback* callbackToRemove) +{ + for (auto& device : MidiInput::getAvailableDevices()) + { + if (device.name == name) + { + removeMidiInputDeviceCallback (device.identifier, callbackToRemove); + return; + } + } +} + +void AudioDeviceManager::setDefaultMidiOutput (const String& name) +{ + for (auto& device : MidiOutput::getAvailableDevices()) + { + if (device.name == name) + { + setDefaultMidiOutputDevice (device.identifier); + return; + } + } +} + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h index 8914dbc..b855d51 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h @@ -197,7 +197,7 @@ public: Note that this can return a null pointer if no settings have been explicitly changed (i.e. if the device manager has just been left in its default state). */ - XmlElement* createStateXml() const; + std::unique_ptr createStateXml() const; //============================================================================== /** Returns the current device properties that are in use. @@ -307,12 +307,12 @@ public: //============================================================================== /** Enables or disables a midi input device. - The list of devices can be obtained with the MidiInput::getDevices() method. + The list of devices can be obtained with the MidiInput::getAvailableDevices() method. Any incoming messages from enabled input devices will be forwarded on to all the - listeners that have been registered with the addMidiInputCallback() method. They - can either register for messages from a particular device, or from just the - "default" midi input. + listeners that have been registered with the addMidiInputDeviceCallback() method. They + can either register for messages from a particular device, or from just the "default" + midi input. Routing the midi input via an AudioDeviceManager means that when a listener registers for the default midi input, this default device can be changed by the @@ -322,62 +322,65 @@ public: or not present, so that when the input is re-enabled, the listener will start receiving messages again. - @see addMidiInputCallback, isMidiInputEnabled + @see addMidiInputDeviceCallback, isMidiInputDeviceEnabled */ - void setMidiInputEnabled (const String& midiInputDeviceName, bool enabled); + void setMidiInputDeviceEnabled (const String& deviceIdentifier, bool enabled); /** Returns true if a given midi input device is being used. - @see setMidiInputEnabled + + @see setMidiInputDeviceEnabled */ - bool isMidiInputEnabled (const String& midiInputDeviceName) const; + bool isMidiInputDeviceEnabled (const String& deviceIdentifier) const; /** Registers a listener for callbacks when midi events arrive from a midi input. - The device name can be empty to indicate that it wants to receive all incoming - events from all the enabled MIDI inputs. Or it can be the name of one of the + The device identifier can be empty to indicate that it wants to receive all incoming + events from all the enabled MIDI inputs. Or it can be the identifier of one of the MIDI input devices if it just wants the events from that device. (see - MidiInput::getDevices() for the list of device names). + MidiInput::getAvailableDevices() for the list of devices). - Only devices which are enabled (see the setMidiInputEnabled() method) will have their + Only devices which are enabled (see the setMidiInputDeviceEnabled() method) will have their events forwarded on to listeners. */ - void addMidiInputCallback (const String& midiInputDeviceName, - MidiInputCallback* callback); + void addMidiInputDeviceCallback (const String& deviceIdentifier, + MidiInputCallback* callback); - /** Removes a listener that was previously registered with addMidiInputCallback(). */ - void removeMidiInputCallback (const String& midiInputDeviceName, - MidiInputCallback* callback); + /** Removes a listener that was previously registered with addMidiInputDeviceCallback(). */ + void removeMidiInputDeviceCallback (const String& deviceIdentifier, + MidiInputCallback* callback); //============================================================================== /** Sets a midi output device to use as the default. - The list of devices can be obtained with the MidiOutput::getDevices() method. + The list of devices can be obtained with the MidiOutput::getAvailableDevices() method. The specified device will be opened automatically and can be retrieved with the getDefaultMidiOutput() method. Pass in an empty string to deselect all devices. For the default device, you - can use MidiOutput::getDevices() [MidiOutput::getDefaultDeviceIndex()]. + can use MidiOutput::getDefaultDevice(). - @see getDefaultMidiOutput, getDefaultMidiOutputName + @see getDefaultMidiOutput, getDefaultMidiOutputIdentifier */ - void setDefaultMidiOutput (const String& deviceName); + void setDefaultMidiOutputDevice (const String& deviceIdentifier); /** Returns the name of the default midi output. - @see setDefaultMidiOutput, getDefaultMidiOutput - */ - const String& getDefaultMidiOutputName() const noexcept { return defaultMidiOutputName; } - /** Returns the current default midi output device. - If no device has been selected, or the device can't be opened, this will return nullptr. - @see getDefaultMidiOutputName + @see setDefaultMidiOutputDevice, getDefaultMidiOutput + */ + const String& getDefaultMidiOutputIdentifier() const noexcept { return defaultMidiOutputDeviceInfo.identifier; } + + /** Returns the current default midi output device. If no device has been selected, or the + device can't be opened, this will return nullptr. + + @see getDefaultMidiOutputIdentifier */ MidiOutput* getDefaultMidiOutput() const noexcept { return defaultMidiOutput.get(); } + //============================================================================== /** Returns a list of the types of device supported. */ const OwnedArray& getAvailableDeviceTypes(); - //============================================================================== /** Creates a list of available types. This will add a set of new AudioIODeviceType objects to the specified list, to @@ -388,10 +391,11 @@ public: */ virtual void createAudioDeviceTypes (OwnedArray& types); - /** Adds a new device type to the list of types. - The manager will take ownership of the object that is passed-in. - */ - void addAudioDeviceType (AudioIODeviceType* newDeviceType); + /** Adds a new device type to the list of types. */ + void addAudioDeviceType (std::unique_ptr newDeviceType); + + /** Removes a previously added device type from the manager. */ + void removeAudioDeviceType (AudioIODeviceType* deviceTypeToRemove); //============================================================================== /** Plays a beep through the current audio device. @@ -461,6 +465,20 @@ public: */ int getXRunCount() const noexcept; + //============================================================================== + /** Deprecated. */ + void setMidiInputEnabled (const String&, bool); + /** Deprecated. */ + bool isMidiInputEnabled (const String&) const; + /** Deprecated. */ + void addMidiInputCallback (const String&, MidiInputCallback*); + /** Deprecated. */ + void removeMidiInputCallback (const String&, MidiInputCallback*); + /** Deprecated. */ + void setDefaultMidiOutput (const String&); + /** Deprecated. */ + const String& getDefaultMidiOutputName() const noexcept { return defaultMidiOutputDeviceInfo.name; } + private: //============================================================================== OwnedArray availableDeviceTypes; @@ -471,22 +489,21 @@ private: Array callbacks; int numInputChansNeeded = 0, numOutputChansNeeded = 2; String preferredDeviceName, currentDeviceType; - BigInteger inputChannels, outputChannels; std::unique_ptr lastExplicitSettings; mutable bool listNeedsScanning = true; AudioBuffer tempBuffer; struct MidiCallbackInfo { - String deviceName; + String deviceIdentifier; MidiInputCallback* callback; }; - StringArray midiInsFromXml; - OwnedArray enabledMidiInputs; + Array midiDeviceInfosFromXml; + std::vector> enabledMidiInputs; Array midiCallbacks; - String defaultMidiOutputName; + MidiDeviceInfo defaultMidiOutputDeviceInfo; std::unique_ptr defaultMidiOutput; CriticalSection audioCallbackLock, midiCallbackLock; diff --git a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.cpp b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.cpp index 11636db..a7f816e 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.cpp @@ -149,7 +149,6 @@ installed, or you've not got your paths set up correctly to find its header files. */ - #include #include #include #endif @@ -179,7 +178,7 @@ #include "audio_io/juce_AudioIODevice.cpp" #include "audio_io/juce_AudioIODeviceType.cpp" #include "midi_io/juce_MidiMessageCollector.cpp" -#include "midi_io/juce_MidiOutput.cpp" +#include "midi_io/juce_MidiDevices.cpp" #include "sources/juce_AudioSourcePlayer.cpp" #include "sources/juce_AudioTransportSource.cpp" #include "native/juce_MidiDataConcatenator.h" diff --git a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h index bd05fa1..1209d3c 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h +++ b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h @@ -20,6 +20,7 @@ ============================================================================== */ + /******************************************************************************* The block below describes the properties of this module, and is read by the Projucer to automatically generate project code that uses it. @@ -29,19 +30,19 @@ BEGIN_JUCE_MODULE_DECLARATION - ID: juce_audio_devices - vendor: juce - version: 5.4.3 - name: JUCE audio and MIDI I/O device classes - description: Classes to play and record from audio and MIDI I/O devices - website: http://www.juce.com/juce - license: ISC + ID: juce_audio_devices + vendor: juce + version: 5.4.7 + name: JUCE audio and MIDI I/O device classes + description: Classes to play and record from audio and MIDI I/O devices + website: http://www.juce.com/juce + license: ISC - dependencies: juce_audio_basics, juce_events - OSXFrameworks: CoreAudio CoreMIDI AudioToolbox - iOSFrameworks: CoreAudio CoreMIDI AudioToolbox AVFoundation - linuxPackages: alsa - mingwLibs: winmm + dependencies: juce_audio_basics, juce_events + OSXFrameworks: CoreAudio CoreMIDI AudioToolbox + iOSFrameworks: CoreAudio CoreMIDI AudioToolbox AVFoundation + linuxPackages: alsa + mingwLibs: winmm END_JUCE_MODULE_DECLARATION @@ -63,7 +64,7 @@ Enables the use of the Windows Runtime API for MIDI, allowing connections to Bluetooth Low Energy devices on Windows 10 version 1809 (October 2018 Update) and later. If you enable this flag then older, unsupported, - versions of Windows will automatically fall back to using the regualar + versions of Windows will automatically fall back to using the regular Win32 MIDI API. You will need version 10.0.14393.0 of the Windows Standalone SDK to compile @@ -99,7 +100,7 @@ Enables WASAPI audio devices in exclusive mode (Windows Vista and above). */ #ifndef JUCE_WASAPI_EXCLUSIVE - #define JUCE_WASAPI_EXCLUSIVE 1 + #define JUCE_WASAPI_EXCLUSIVE 0 #endif @@ -171,9 +172,8 @@ #endif //============================================================================== -#include "midi_io/juce_MidiInput.h" +#include "midi_io/juce_MidiDevices.h" #include "midi_io/juce_MidiMessageCollector.h" -#include "midi_io/juce_MidiOutput.h" #include "audio_io/juce_AudioIODevice.h" #include "audio_io/juce_AudioIODeviceType.h" #include "audio_io/juce_SystemAudioVolume.h" diff --git a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.cpp b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.cpp similarity index 88% rename from JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.cpp rename to JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.cpp index a707997..a5f761a 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.cpp @@ -23,18 +23,8 @@ namespace juce { -struct MidiOutput::PendingMessage -{ - PendingMessage (const void* data, int len, double timeStamp) - : message (data, len, timeStamp) - {} - - MidiMessage message; - PendingMessage* next; -}; - -MidiOutput::MidiOutput (const String& deviceName) - : Thread ("midi out"), name (deviceName) +MidiOutput::MidiOutput (const String& deviceName, const String& deviceIdentifier) + : Thread ("midi out"), deviceInfo (deviceName, deviceIdentifier) { } @@ -116,7 +106,7 @@ void MidiOutput::run() { while (! threadShouldExit()) { - uint32 now = Time::getMillisecondCounter(); + auto now = Time::getMillisecondCounter(); uint32 eventTime = 0; uint32 timeToWait = 500; diff --git a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.h b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.h new file mode 100644 index 0000000..054a376 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.h @@ -0,0 +1,373 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2017 - ROLI Ltd. + + JUCE is an open source library subject to commercial or open-source + licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. 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. + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ +//============================================================================== +/** + This struct contains information about a MIDI input or output device. + + You can get one of these structs by calling the static getAvailableDevices() or + getDefaultDevice() methods of MidiInput and MidiOutput or by calling getDeviceInfo() + on an instance of these classes. Devices can be opened by passing the identifier to + the openDevice() method. + + @tags{Audio} +*/ +struct MidiDeviceInfo +{ + MidiDeviceInfo() = default; + + MidiDeviceInfo (const String& deviceName, const String& deviceIdentifier) + : name (deviceName), identifier (deviceIdentifier) + { + } + + /** The name of this device. + + This will be provided by the OS unless the device has been created with the + createNewDevice() method. + + Note that the name is not guaranteed to be unique and two devices with the + same name will be indistinguishable. If you want to address a specific device + it is better to use the identifier. + */ + String name; + + /** The identifier for this device. + + This will be provided by the OS and it's format will differ on different systems + e.g. on macOS it will be a number whereas on Windows it will be a long alphanumeric string. + */ + String identifier; + + //============================================================================== + bool operator== (const MidiDeviceInfo& other) const noexcept { return name == other.name && identifier == other.identifier; } + bool operator!= (const MidiDeviceInfo& other) const noexcept { return ! operator== (other); } +}; + +class MidiInputCallback; + +//============================================================================== +/** + Represents a midi input device. + + To create one of these, use the static getAvailableDevices() method to find out what + inputs are available, and then use the openDevice() method to try to open one. + + @see MidiOutput + + @tags{Audio} +*/ +class JUCE_API MidiInput final +{ +public: + //============================================================================== + /** Returns a list of the available midi input devices. + + You can open one of the devices by passing its identifier into the openDevice() method. + + @see MidiDeviceInfo, getDevices, getDefaultDeviceIndex, openDevice + */ + static Array getAvailableDevices(); + + /** Returns the MidiDeviceInfo of the default midi input device to use. */ + static MidiDeviceInfo getDefaultDevice(); + + /** Tries to open one of the midi input devices. + + This will return a MidiInput object if it manages to open it, you can then + call start() and stop() on this device. + + If the device can't be opened, this will return an empty object. + + @param deviceIdentifier the ID of the device to open - use the getAvailableDevices() method to + find the available devices that can be opened + @param callback the object that will receive the midi messages from this device + + @see MidiInputCallback, getDevices + */ + static std::unique_ptr openDevice (const String& deviceIdentifier, MidiInputCallback* callback); + + #if JUCE_LINUX || JUCE_MAC || JUCE_IOS || DOXYGEN + /** This will try to create a new midi input device (only available on Linux, macOS and iOS). + + This will attempt to create a new midi input device with the specified name for other + apps to connect to. + + NB - if you are calling this method on iOS you must have enabled the "Audio Background Capability" + setting in the iOS exporter otherwise this method will fail. + + Returns an empty object if a device can't be created. + + @param deviceName the name of the device to create + @param callback the object that will receive the midi messages from this device + */ + static std::unique_ptr createNewDevice (const String& deviceName, MidiInputCallback* callback); + #endif + + //============================================================================== + /** Destructor. */ + ~MidiInput(); + + /** Starts the device running. + + After calling this, the device will start sending midi messages to the MidiInputCallback + object that was specified when the openDevice() method was called. + + @see stop + */ + void start(); + + /** Stops the device running. + + @see start + */ + void stop(); + + /** Returns the MidiDeviceInfo struct containing some information about this device. */ + MidiDeviceInfo getDeviceInfo() const noexcept { return deviceInfo; } + + /** Returns the identifier of this device. */ + String getIdentifier() const noexcept { return deviceInfo.identifier; } + + /** Returns the name of this device. */ + String getName() const noexcept { return deviceInfo.name; } + + /** Sets a custom name for the device. */ + void setName (const String& newName) noexcept { deviceInfo.name = newName; } + + //============================================================================== + /** Deprecated. */ + static StringArray getDevices(); + /** Deprecated. */ + static int getDefaultDeviceIndex(); + /** Deprecated. */ + static std::unique_ptr openDevice (int, MidiInputCallback*); + +private: + //============================================================================== + explicit MidiInput (const String&, const String&); + + MidiDeviceInfo deviceInfo; + void* internal = nullptr; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInput) +}; + +//============================================================================== +/** + Receives incoming messages from a physical MIDI input device. + + This class is overridden to handle incoming midi messages. See the MidiInput + class for more details. + + @see MidiInput + + @tags{Audio} +*/ +class JUCE_API MidiInputCallback +{ +public: + /** Destructor. */ + virtual ~MidiInputCallback() = default; + + /** Receives an incoming message. + + A MidiInput object will call this method when a midi event arrives. It'll be + called on a high-priority system thread, so avoid doing anything time-consuming + in here, and avoid making any UI calls. You might find the MidiBuffer class helpful + for queueing incoming messages for use later. + + @param source the MidiInput object that generated the message + @param message the incoming message. The message's timestamp is set to a value + equivalent to (Time::getMillisecondCounter() / 1000.0) to specify the + time when the message arrived + */ + virtual void handleIncomingMidiMessage (MidiInput* source, + const MidiMessage& message) = 0; + + /** Notification sent each time a packet of a multi-packet sysex message arrives. + + If a long sysex message is broken up into multiple packets, this callback is made + for each packet that arrives until the message is finished, at which point + the normal handleIncomingMidiMessage() callback will be made with the entire + message. + + The message passed in will contain the start of a sysex, but won't be finished + with the terminating 0xf7 byte. + */ + virtual void handlePartialSysexMessage (MidiInput* source, + const uint8* messageData, + int numBytesSoFar, + double timestamp) + { + ignoreUnused (source, messageData, numBytesSoFar, timestamp); + } +}; + +//============================================================================== +/** + Represents a midi output device. + + To create one of these, use the static getAvailableDevices() method to find out what + outputs are available, and then use the openDevice() method to try to open one. + + @see MidiInput + + @tags{Audio} +*/ +class JUCE_API MidiOutput final : private Thread +{ +public: + //============================================================================== + /** Returns a list of the available midi output devices. + + You can open one of the devices by passing its identifier into the openDevice() method. + + @see MidiDeviceInfo, getDevices, getDefaultDeviceIndex, openDevice + */ + static Array getAvailableDevices(); + + /** Returns the MidiDeviceInfo of the default midi output device to use. */ + static MidiDeviceInfo getDefaultDevice(); + + /** Tries to open one of the midi output devices. + + This will return a MidiOutput object if it manages to open it, you can then + send messages to this device. + + If the device can't be opened, this will return an empty object. + + @param deviceIdentifier the ID of the device to open - use the getAvailableDevices() method to + find the available devices that can be opened + @see getDevices + */ + static std::unique_ptr openDevice (const String& deviceIdentifier); + + #if JUCE_LINUX || JUCE_MAC || JUCE_IOS || DOXYGEN + /** This will try to create a new midi output device (only available on Linux, macOS and iOS). + + This will attempt to create a new midi output device with the specified name that other + apps can connect to and use as their midi input. + + NB - if you are calling this method on iOS you must have enabled the "Audio Background Capability" + setting in the iOS exporter otherwise this method will fail. + + Returns an empty object if a device can't be created. + + @param deviceName the name of the device to create + */ + static std::unique_ptr createNewDevice (const String& deviceName); + #endif + + //============================================================================== + /** Destructor. */ + ~MidiOutput() override; + + /** Returns the MidiDeviceInfo struct containing some information about this device. */ + MidiDeviceInfo getDeviceInfo() const noexcept { return deviceInfo; } + + /** Returns the identifier of this device. */ + String getIdentifier() const noexcept { return deviceInfo.identifier; } + + /** Returns the name of this device. */ + String getName() const noexcept { return deviceInfo.name; } + + /** Sets a custom name for the device. */ + void setName (const String& newName) noexcept { deviceInfo.name = newName; } + + //============================================================================== + /** 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. + + The MidiOutput class has an internal thread that can send out timestamped + messages - this appends a set of messages to its internal buffer, ready for + sending. + + This will only work if you've already started the thread with startBackgroundThread(). + + A time is specified, at which the block of messages should be sent. This time uses + the same time base as Time::getMillisecondCounter(), and must be in the future. + + The samplesPerSecondForBuffer parameter indicates the number of samples per second + used by the MidiBuffer. Each event in a MidiBuffer has a sample position, and the + samplesPerSecondForBuffer value is needed to convert this sample position to a + real time. + */ + void sendBlockOfMessages (const MidiBuffer& buffer, + double millisecondCounterToStartAt, + double samplesPerSecondForBuffer); + + /** Gets rid of any midi messages that had been added by sendBlockOfMessages(). */ + void clearAllPendingMessages(); + + /** Starts up a background thread so that the device can send blocks of data. + Call this to get the device ready, before using sendBlockOfMessages(). + */ + void startBackgroundThread(); + + /** Stops the background thread, and clears any pending midi events. + @see startBackgroundThread + */ + void stopBackgroundThread(); + + //============================================================================== + /** Deprecated. */ + static StringArray getDevices(); + /** Deprecated. */ + static int getDefaultDeviceIndex(); + /** Deprecated. */ + static std::unique_ptr openDevice (int); + +private: + //============================================================================== + struct PendingMessage + { + PendingMessage (const void* data, int len, double timeStamp) + : message (data, len, timeStamp) + { + } + + MidiMessage message; + PendingMessage* next; + }; + + //============================================================================== + explicit MidiOutput (const String&, const String&); + void run() override; + + MidiDeviceInfo deviceInfo; + void* internal = nullptr; + CriticalSection lock; + PendingMessage* firstMessage = nullptr; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutput) +}; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiInput.h b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiInput.h deleted file mode 100644 index 49af5bf..0000000 --- a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiInput.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. 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. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -class MidiInput; - - -//============================================================================== -/** - Receives incoming messages from a physical MIDI input device. - - This class is overridden to handle incoming midi messages. See the MidiInput - class for more details. - - @see MidiInput - - @tags{Audio} -*/ -class JUCE_API MidiInputCallback -{ -public: - /** Destructor. */ - virtual ~MidiInputCallback() = default; - - - /** Receives an incoming message. - - A MidiInput object will call this method when a midi event arrives. It'll be - called on a high-priority system thread, so avoid doing anything time-consuming - in here, and avoid making any UI calls. You might find the MidiBuffer class helpful - for queueing incoming messages for use later. - - @param source the MidiInput object that generated the message - @param message the incoming message. The message's timestamp is set to a value - equivalent to (Time::getMillisecondCounter() / 1000.0) to specify the - time when the message arrived. - */ - virtual void handleIncomingMidiMessage (MidiInput* source, - const MidiMessage& message) = 0; - - /** Notification sent each time a packet of a multi-packet sysex message arrives. - - If a long sysex message is broken up into multiple packets, this callback is made - for each packet that arrives until the message is finished, at which point - the normal handleIncomingMidiMessage() callback will be made with the entire - message. - - The message passed in will contain the start of a sysex, but won't be finished - with the terminating 0xf7 byte. - */ - virtual void handlePartialSysexMessage (MidiInput* source, - const uint8* messageData, - int numBytesSoFar, - double timestamp) - { - ignoreUnused (source, messageData, numBytesSoFar, timestamp); - } -}; - -//============================================================================== -/** - Represents a midi input device. - - To create one of these, use the static getDevices() method to find out what inputs are - available, and then use the openDevice() method to try to open one. - - @see MidiOutput - - @tags{Audio} -*/ -class JUCE_API MidiInput final -{ -public: - //============================================================================== - /** Returns a list of the available midi input devices. - - You can open one of the devices by passing its index into the - openDevice() method. - - @see getDefaultDeviceIndex, openDevice - */ - static StringArray getDevices(); - - /** Returns the index of the default midi input device to use. - - This refers to the index in the list returned by getDevices(). - */ - static int getDefaultDeviceIndex(); - - /** Tries to open one of the midi input devices. - - This will return a MidiInput object if it manages to open it. You can then - call start() and stop() on this device, and delete it when no longer needed. - - If the device can't be opened, this will return a null pointer. - - @param deviceIndex the index of a device from the list returned by getDevices() - @param callback the object that will receive the midi messages from this device. - - @see MidiInputCallback, getDevices - */ - static MidiInput* openDevice (int deviceIndex, - MidiInputCallback* callback); - - #if JUCE_LINUX || JUCE_MAC || JUCE_IOS || DOXYGEN - /** This will try to create a new midi input device (Not available on Windows). - - This will attempt to create a new midi input device with the specified name, - for other apps to connect to. - - Returns nullptr if a device can't be created. - - @param deviceName the name to use for the new device - @param callback the object that will receive the midi messages from this device. - */ - static MidiInput* createNewDevice (const String& deviceName, - MidiInputCallback* callback); - #endif - - //============================================================================== - /** Destructor. */ - ~MidiInput(); - - /** Returns the name of this device. */ - const String& getName() const noexcept { return name; } - - /** Allows you to set a custom name for the device, in case you don't like the name - it was given when created. - */ - void setName (const String& newName) noexcept { name = newName; } - - //============================================================================== - /** Starts the device running. - - After calling this, the device will start sending midi messages to the - MidiInputCallback object that was specified when the openDevice() method - was called. - - @see stop - */ - void start(); - - /** Stops the device running. - @see start - */ - void stop(); - -private: - //============================================================================== - String name; - void* internal = nullptr; - - // The input objects are created with the openDevice() method. - explicit MidiInput (const String&); - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInput) -}; - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.cpp b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.cpp index 50ca205..97f7330 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.cpp @@ -34,9 +34,10 @@ MidiMessageCollector::~MidiMessageCollector() //============================================================================== void MidiMessageCollector::reset (const double newSampleRate) { + const ScopedLock sl (midiCallbackLock); + jassert (newSampleRate > 0); - const ScopedLock sl (midiCallbackLock); #if JUCE_DEBUG hasCalledReset = true; #endif @@ -47,6 +48,8 @@ void MidiMessageCollector::reset (const double newSampleRate) void MidiMessageCollector::addMessageToQueue (const MidiMessage& message) { + const ScopedLock sl (midiCallbackLock); + #if JUCE_DEBUG jassert (hasCalledReset); // you need to call reset() to set the correct sample rate before using this object #endif @@ -55,8 +58,6 @@ void MidiMessageCollector::addMessageToQueue (const MidiMessage& message) // for details of what the number should be. jassert (message.getTimeStamp() != 0); - const ScopedLock sl (midiCallbackLock); - auto sampleNumber = (int) ((message.getTimeStamp() - 0.001 * lastCallbackTime) * sampleRate); incomingMessages.addEvent (message, sampleNumber); @@ -70,6 +71,8 @@ void MidiMessageCollector::addMessageToQueue (const MidiMessage& message) void MidiMessageCollector::removeNextBlockOfMessages (MidiBuffer& destBuffer, const int numSamples) { + const ScopedLock sl (midiCallbackLock); + #if JUCE_DEBUG jassert (hasCalledReset); // you need to call reset() to set the correct sample rate before using this object #endif @@ -79,7 +82,6 @@ void MidiMessageCollector::removeNextBlockOfMessages (MidiBuffer& destBuffer, auto timeNow = Time::getMillisecondCounterHiRes(); auto msElapsed = timeNow - lastCallbackTime; - const ScopedLock sl (midiCallbackLock); lastCallbackTime = timeNow; if (! incomingMessages.isEmpty()) diff --git a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.h b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.h deleted file mode 100644 index c41dde7..0000000 --- a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. 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. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - Controls a physical MIDI output device. - - To create one of these, use the static getDevices() method to get a list of the - available output devices, then use the openDevice() method to try to open one. - - @see MidiInput - - @tags{Audio} -*/ -class JUCE_API MidiOutput final : private Thread -{ -public: - //============================================================================== - /** Returns a list of the available midi output devices. - - You can open one of the devices by passing its index into the - openDevice() method. - - @see getDefaultDeviceIndex, openDevice - */ - static StringArray getDevices(); - - /** Returns the index of the default midi output device to use. - - This refers to the index in the list returned by getDevices(). - */ - static int getDefaultDeviceIndex(); - - /** Tries to open one of the midi output devices. - - This will return a MidiOutput object if it manages to open it. You can then - send messages to this device, and delete it when no longer needed. - - If the device can't be opened, this will return a null pointer. - - @param deviceIndex the index of a device from the list returned by getDevices() - @see getDevices - */ - static MidiOutput* openDevice (int deviceIndex); - - - #if JUCE_LINUX || JUCE_MAC || JUCE_IOS || DOXYGEN - /** This will try to create a new midi output device (Not available on Windows). - - This will attempt to create a new midi output device that other apps can connect - to and use as their midi input. - - Returns nullptr if a device can't be created. - - @param deviceName the name to use for the new device - */ - static MidiOutput* createNewDevice (const String& deviceName); - #endif - - //============================================================================== - /** Destructor. */ - ~MidiOutput() override; - - /** Returns the name of this device. */ - const String& getName() const noexcept { return name; } - - /** 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. - - The MidiOutput class has an internal thread that can send out timestamped - messages - this appends a set of messages to its internal buffer, ready for - sending. - - This will only work if you've already started the thread with startBackgroundThread(). - - A time is specified, at which the block of messages should be sent. This time uses - the same time base as Time::getMillisecondCounter(), and must be in the future. - - The samplesPerSecondForBuffer parameter indicates the number of samples per second - used by the MidiBuffer. Each event in a MidiBuffer has a sample position, and the - samplesPerSecondForBuffer value is needed to convert this sample position to a - real time. - */ - void sendBlockOfMessages (const MidiBuffer& buffer, - double millisecondCounterToStartAt, - double samplesPerSecondForBuffer); - - /** Gets rid of any midi messages that had been added by sendBlockOfMessages(). */ - void clearAllPendingMessages(); - - /** Starts up a background thread so that the device can send blocks of data. - Call this to get the device ready, before using sendBlockOfMessages(). - */ - void startBackgroundThread(); - - /** Stops the background thread, and clears any pending midi events. - @see startBackgroundThread - */ - void stopBackgroundThread(); - - -private: - //============================================================================== - void* internal = nullptr; - CriticalSection lock; - struct PendingMessage; - PendingMessage* firstMessage = nullptr; - String name; - - MidiOutput (const String& midiName); // These objects are created with the openDevice() method. - void run() override; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutput) -}; - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/java/app/com/roli/juce/JuceMidiSupport.java b/JuceLibraryCode/modules/juce_audio_devices/native/java/app/com/roli/juce/JuceMidiSupport.java index cfe9918..713f335 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/java/app/com/roli/juce/JuceMidiSupport.java +++ b/JuceLibraryCode/modules/juce_audio_devices/native/java/app/com/roli/juce/JuceMidiSupport.java @@ -51,6 +51,8 @@ public class JuceMidiSupport // send will do nothing on an input port void sendMidi (byte[] msg, int offset, int count); + + String getName (); } //============================================================================== @@ -256,6 +258,12 @@ public class JuceMidiSupport { } + @Override + public String getName () + { + return owner.getPortName (portPath); + } + MidiDeviceManager owner; MidiOutputPort androidPort; MidiPortPath portPath; @@ -331,6 +339,12 @@ public class JuceMidiSupport androidPort = null; } + @Override + public String getName () + { + return owner.getPortName (portPath); + } + MidiDeviceManager owner; MidiInputPort androidPort; MidiPortPath portPath; @@ -343,7 +357,6 @@ public class JuceMidiSupport deviceId = deviceIdToUse; isInput = direction; portIndex = androidIndex; - } public int deviceId; @@ -555,17 +568,17 @@ public class JuceMidiSupport super.finalize (); } - public String[] getJuceAndroidMidiInputDevices () + public String[] getJuceAndroidMidiOutputDeviceNameAndIDs () { - return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_OUTPUT); + return getJuceAndroidMidiDeviceNameAndIDs (MidiDeviceInfo.PortInfo.TYPE_OUTPUT); } - public String[] getJuceAndroidMidiOutputDevices () + public String[] getJuceAndroidMidiInputDeviceNameAndIDs () { - return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_INPUT); + return getJuceAndroidMidiDeviceNameAndIDs (MidiDeviceInfo.PortInfo.TYPE_INPUT); } - private String[] getJuceAndroidMidiDevices (int portType) + private String[] getJuceAndroidMidiDeviceNameAndIDs (int portType) { // only update the list when JUCE asks for a new list synchronized (MidiDeviceManager.class) @@ -573,22 +586,24 @@ public class JuceMidiSupport deviceInfos = getDeviceInfos (); } - ArrayList portNames = new ArrayList (); + ArrayList portNameAndIDs = new ArrayList (); - int index = 0; - for (MidiPortPath portInfo = getPortPathForJuceIndex (portType, index); portInfo != null; portInfo = getPortPathForJuceIndex (portType, ++index)) - portNames.add (getPortName (portInfo)); + for (MidiPortPath portInfo : getAllPorts (portType)) + { + portNameAndIDs.add (getPortName (portInfo)); + portNameAndIDs.add (Integer.toString (portInfo.hashCode ())); + } - String[] names = new String[portNames.size ()]; - return portNames.toArray (names); + String[] names = new String[portNameAndIDs.size ()]; + return portNameAndIDs.toArray (names); } - private JuceMidiPort openMidiPortWithJuceIndex (int index, long host, boolean isInput) + private JuceMidiPort openMidiPortWithID (int deviceID, long host, boolean isInput) { synchronized (MidiDeviceManager.class) { - int portTypeToFind = (isInput ? MidiDeviceInfo.PortInfo.TYPE_OUTPUT : MidiDeviceInfo.PortInfo.TYPE_INPUT); - MidiPortPath portInfo = getPortPathForJuceIndex (portTypeToFind, index); + int portTypeToFind = (isInput ? MidiDeviceInfo.PortInfo.TYPE_INPUT : MidiDeviceInfo.PortInfo.TYPE_OUTPUT); + MidiPortPath portInfo = getPortPathForID (portTypeToFind, deviceID); if (portInfo != null) { @@ -633,14 +648,14 @@ public class JuceMidiSupport return null; } - public JuceMidiPort openMidiInputPortWithJuceIndex (int index, long host) + public JuceMidiPort openMidiInputPortWithID (int deviceID, long host) { - return openMidiPortWithJuceIndex (index, host, true); + return openMidiPortWithID (deviceID, host, true); } - public JuceMidiPort openMidiOutputPortWithJuceIndex (int index) + public JuceMidiPort openMidiOutputPortWithID (int deviceID) { - return openMidiPortWithJuceIndex (index, 0, false); + return openMidiPortWithID (deviceID, 0, false); } /* 0: unpaired, 1: paired, 2: pairing */ @@ -773,24 +788,6 @@ public class JuceMidiSupport openPorts.remove (path); } - public String getInputPortNameForJuceIndex (int index) - { - MidiPortPath portInfo = getPortPathForJuceIndex (MidiDeviceInfo.PortInfo.TYPE_OUTPUT, index); - if (portInfo != null) - return getPortName (portInfo); - - return ""; - } - - public String getOutputPortNameForJuceIndex (int index) - { - MidiPortPath portInfo = getPortPathForJuceIndex (MidiDeviceInfo.PortInfo.TYPE_INPUT, index); - if (portInfo != null) - return getPortName (portInfo); - - return ""; - } - public void onDeviceAdded (MidiDeviceInfo info) { // only add standard midi devices @@ -980,24 +977,24 @@ public class JuceMidiSupport return ""; } - public MidiPortPath getPortPathForJuceIndex (int portType, int juceIndex) + public ArrayList getAllPorts (int portType) { - int portIdx = 0; - for (MidiDeviceInfo info : deviceInfos) - { - for (MidiDeviceInfo.PortInfo portInfo : info.getPorts ()) - { - if (portInfo.getType () == portType) - { - if (portIdx == juceIndex) - return new MidiPortPath (info.getId (), - (portType == MidiDeviceInfo.PortInfo.TYPE_INPUT), - portInfo.getPortNumber ()); + ArrayList ports = new ArrayList (); - portIdx++; - } - } - } + for (MidiDeviceInfo info : deviceInfos) + for (MidiDeviceInfo.PortInfo portInfo : info.getPorts ()) + if (portInfo.getType () == portType) + ports.add (new MidiPortPath (info.getId (), (portType == MidiDeviceInfo.PortInfo.TYPE_INPUT), + portInfo.getPortNumber ())); + + return ports; + } + + public MidiPortPath getPortPathForID (int portType, int deviceID) + { + for (MidiPortPath port : getAllPorts (portType)) + if (port.hashCode () == deviceID) + return port; return null; } diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h b/JuceLibraryCode/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h index d8a1cbc..6fbe67a 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h @@ -116,7 +116,7 @@ private: do { - if (pendingSysexSize > 0 && isInitialByte (*d)) + if (pendingSysexSize > 0 && isStatusByte (*d)) { if (*d == 0xf7) { @@ -172,7 +172,8 @@ private: } static bool isRealtimeMessage (uint8 byte) { return byte >= 0xf8 && byte <= 0xfe; } - static bool isInitialByte (uint8 byte) { return byte >= 0x80 && byte != 0xf7; } + static bool isStatusByte (uint8 byte) { return byte >= 0x80; } + static bool isInitialByte (uint8 byte) { return isStatusByte (byte) && byte != 0xf7; } uint8 currentMessage[3]; int currentMessageLen = 0; diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp index 3fa7399..ecfa7b9 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp @@ -73,7 +73,7 @@ public: AndroidAudioIODevice (const String& deviceName) : AudioIODevice (deviceName, javaAudioTypeName), Thread ("audio"), - minBufferSizeOut (0), minBufferSizeIn (0), callback (0), sampleRate (0), + minBufferSizeOut (0), minBufferSizeIn (0), callback (nullptr), sampleRate (0), numClientInputChannels (0), numDeviceInputChannels (0), numDeviceInputChannelsAvailable (2), numClientOutputChannels (0), numDeviceOutputChannels (0), actualBufferSize (0), isRunning (false), @@ -100,7 +100,7 @@ public: << sampleRate << " Hz; input chans: " << numDeviceInputChannelsAvailable); } - ~AndroidAudioIODevice() + ~AndroidAudioIODevice() override { close(); } @@ -196,7 +196,7 @@ public: (jint) (minBufferSizeOut * numDeviceOutputChannels * static_cast (sizeof (int16))), MODE_STREAM))); const bool supportsUnderrunCount = (getAndroidSDKVersion() >= 24); - getUnderrunCount = supportsUnderrunCount ? env->GetMethodID (AudioTrack, "getUnderrunCount", "()I") : 0; + getUnderrunCount = supportsUnderrunCount ? env->GetMethodID (AudioTrack, "getUnderrunCount", "()I") : nullptr; int outputDeviceState = env->CallIntMethod (outputDevice, AudioTrack.getState); if (outputDeviceState > 0) @@ -282,11 +282,11 @@ public: BigInteger getActiveOutputChannels() const override { return activeOutputChans; } BigInteger getActiveInputChannels() const override { return activeInputChans; } String getLastError() override { return lastError; } - bool isPlaying() override { return isRunning && callback != 0; } + bool isPlaying() override { return isRunning && callback != nullptr; } int getXRunCount() const noexcept override { - if (outputDevice != nullptr && getUnderrunCount != 0) + if (outputDevice != nullptr && getUnderrunCount != nullptr) return getEnv()->CallIntMethod (outputDevice, getUnderrunCount); return -1; @@ -337,7 +337,7 @@ public: DBG ("Audio read under-run! " << numRead); } - jshort* const src = env->GetShortArrayElements (audioBuffer, 0); + jshort* const src = env->GetShortArrayElements (audioBuffer, nullptr); for (int chan = 0; chan < inputChannelBuffer.getNumChannels(); ++chan) { @@ -380,7 +380,7 @@ public: if (threadShouldExit()) break; - jshort* const dest = env->GetShortArrayElements (audioBuffer, 0); + jshort* const dest = env->GetShortArrayElements (audioBuffer, nullptr); for (int chan = 0; chan < numDeviceOutputChannels; ++chan) { @@ -417,7 +417,7 @@ private: BigInteger activeOutputChans, activeInputChans; GlobalRef outputDevice, inputDevice; AudioBuffer inputChannelBuffer, outputChannelBuffer; - jmethodID getUnderrunCount = 0; + jmethodID getUnderrunCount = nullptr; void closeDevices() { diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Midi.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Midi.cpp index 449ff1c..2c63160 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Midi.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Midi.cpp @@ -27,339 +27,325 @@ namespace juce // This byte-code is generated from native/java/com/roli/juce/JuceMidiSupport.java with min sdk version 23 // See juce_core/native/java/README.txt on how to generate this byte-code. static const uint8 javaMidiByteCode[] = -{31,139,8,8,173,175,226,91,0,3,106,117,99,101,95,97,117,100,105,111,95,100,101,118,105,99,101,115,46,100,101,120,0,149, -124,11,124,220,69,181,255,153,223,99,119,179,217,164,155,77,218,164,105,178,217,164,73,179,133,60,155,182,164,77,26,154, -164,105,155,118,251,160,217,22,105,144,186,77,182,237,150,100,55,236,110,250,0,175,148,135,180,32,42,42,143,42,92,254,40, -175,130,168,232,5,46,34,42,8,87,20,69,184,92,212,122,125,1,194,223,130,200,67,65,68,196,222,239,153,153,125,164,45,84, -203,231,187,103,126,103,206,156,153,57,115,230,204,153,223,46,25,141,238,113,183,117,44,160,7,183,254,71,91,73,96,244, -225,231,218,251,238,107,108,49,191,55,239,138,244,25,191,14,80,247,85,235,137,38,136,104,207,166,249,62,210,255,30,90,71, -116,150,80,252,1,224,25,155,104,19,232,17,7,81,0,244,117,55,209,93,76,11,137,10,64,215,151,128,223,3,121,104,216,209,76, -180,19,24,7,146,192,36,112,61,112,35,112,8,184,27,184,7,120,0,120,8,120,26,40,110,33,90,7,156,11,196,129,36,176,27,56,8, -124,23,248,30,240,3,224,151,192,107,192,123,64,105,43,209,12,160,18,168,1,234,129,83,128,22,160,3,88,4,108,1,46,5,110,7, -158,6,204,54,162,54,96,24,216,7,124,25,248,9,240,6,224,111,39,234,7,206,1,46,4,190,2,60,13,188,3,84,207,35,90,11,236,7, -238,0,126,2,188,1,56,58,136,170,128,5,192,32,240,97,32,9,252,16,120,23,104,153,79,180,21,184,10,248,13,80,191,128,232,76, -224,227,192,109,192,227,192,203,64,209,66,162,38,96,37,176,5,72,2,251,129,235,129,175,1,223,7,94,0,204,211,136,102,2,109, -192,48,144,0,46,1,14,2,119,1,15,3,63,7,142,0,127,3,90,59,137,150,2,67,192,78,224,223,128,135,128,103,128,231,129,130,69, -68,30,192,11,76,7,102,1,179,129,70,160,9,152,7,44,2,122,128,126,96,16,88,7,108,2,62,12,68,128,81,96,7,48,6,76,0,87,3,143, -0,47,1,255,0,74,23,195,55,128,38,96,1,176,24,232,7,206,0,206,1,118,2,73,224,82,224,38,224,30,224,251,192,175,128,87,129, -119,0,179,11,227,3,170,129,122,96,30,176,24,88,5,156,1,124,8,136,0,113,96,15,240,113,224,83,192,13,192,205,192,109,192, -55,128,7,128,71,129,159,0,191,6,94,6,222,1,236,110,216,0,168,2,234,129,86,96,49,176,19,72,3,215,1,247,3,223,5,158,0,126, -1,252,22,120,25,120,13,120,23,40,95,66,52,31,88,13,124,4,56,31,248,12,112,61,240,85,224,155,192,15,129,159,2,191,1,10, -176,95,188,64,121,143,218,59,61,192,89,192,94,224,11,192,189,192,19,192,207,128,183,0,227,116,162,18,224,20,96,62,208,15, -132,129,109,64,2,248,24,176,31,248,12,112,29,240,37,224,86,224,78,224,235,192,127,2,223,1,126,8,252,12,248,29,240,10,240, -22,96,44,133,31,0,51,128,90,160,21,152,15,116,3,43,129,51,128,51,129,17,96,23,112,25,240,57,224,58,224,6,224,139,192,237, -192,87,129,251,129,239,0,143,0,63,2,126,11,252,21,40,238,37,106,0,122,128,245,192,8,144,4,46,6,62,15,220,9,60,12,60,14, -28,6,94,6,172,62,34,31,16,0,130,64,15,48,4,108,1,226,192,167,128,175,1,143,3,255,11,188,8,188,6,252,13,16,253,216,87,64, -25,48,23,24,0,206,1,70,129,113,96,15,112,49,112,37,112,29,240,69,224,110,224,1,224,113,224,73,224,247,192,107,192,187, -128,107,25,246,6,112,42,48,31,232,6,250,128,21,192,102,32,1,236,1,46,6,174,0,174,2,238,2,238,3,30,3,158,1,94,0,222,0,222, -6,254,1,20,32,184,86,0,1,96,14,208,6,116,1,189,192,32,16,6,62,12,164,129,79,3,119,0,119,3,211,16,115,203,128,58,96,54,80, -15,52,0,115,128,70,32,8,204,5,78,1,78,5,154,0,132,89,66,216,36,132,65,66,184,35,132,53,66,8,35,132,41,66,104,34,132,33, -66,136,33,132,13,66,104,32,108,93,194,214,35,108,15,130,123,19,92,150,224,42,132,101,33,152,151,96,18,90,166,207,135,229, -192,10,96,37,48,8,172,2,86,3,33,96,13,176,22,88,199,231,4,112,6,176,1,24,2,194,164,206,149,15,1,155,129,179,129,115,128, -45,64,4,24,1,70,129,40,112,46,112,1,240,49,224,66,96,31,112,17,112,49,112,9,41,155,100,254,121,53,125,22,147,47,209,229, -35,40,87,130,26,250,153,203,166,46,215,232,242,179,90,198,210,252,90,93,126,93,243,93,121,242,56,2,233,239,154,95,168, -249,179,128,34,110,211,164,248,197,121,125,77,203,43,251,242,228,203,180,60,151,43,242,218,86,230,245,85,165,199,198,50, -126,45,83,163,203,204,175,209,114,30,173,167,78,203,84,235,114,89,147,146,229,114,149,110,91,159,215,182,65,183,229,126, -216,135,130,122,12,45,121,227,108,205,27,91,91,222,216,184,220,214,164,242,2,46,119,54,229,248,25,123,182,231,233,105, -207,27,63,151,151,230,149,51,242,157,121,250,217,15,87,234,126,23,107,62,251,194,18,93,30,211,101,214,57,174,203,235,81, -142,235,242,135,80,78,232,242,104,147,202,105,184,156,70,121,183,46,127,20,229,243,116,249,0,202,73,93,190,10,229,73,93, -190,1,229,93,186,124,75,94,249,238,60,157,15,230,149,61,121,229,71,243,202,63,206,235,247,153,60,254,179,121,229,35,121, -253,190,158,199,255,107,94,91,222,208,123,50,125,53,231,228,43,80,222,171,203,129,230,92,219,165,121,122,218,242,244,119, -230,143,225,212,92,185,169,57,215,215,124,148,211,25,61,40,159,175,203,43,155,115,182,90,143,114,74,151,207,110,86,123, -181,71,175,209,71,117,153,215,232,223,116,57,157,87,110,203,43,103,124,160,87,183,229,114,95,158,63,244,231,249,195,50, -205,159,165,203,150,244,243,54,186,143,20,93,42,184,205,52,250,132,108,219,78,159,146,244,52,250,140,164,46,234,17,236, -183,21,116,5,219,10,189,191,36,169,160,87,37,109,160,217,178,126,14,53,11,142,5,101,82,174,86,243,107,53,127,182,126,102, -122,134,224,125,101,209,181,196,212,75,127,145,84,213,215,235,250,6,61,158,6,68,219,107,36,237,163,59,37,45,167,55,37, -157,79,239,232,122,191,80,52,32,212,190,60,68,76,123,232,15,164,158,231,10,142,247,53,116,21,49,109,160,183,137,227,155, -139,190,39,169,73,143,74,106,211,255,18,199,55,39,221,40,105,29,61,168,233,255,176,205,112,82,220,160,233,87,37,181,232, -191,36,93,67,11,161,223,6,223,73,28,251,86,208,42,193,116,1,173,21,156,247,43,190,59,75,221,116,157,164,5,180,28,245,30, -173,167,72,215,23,129,115,157,164,133,180,76,40,58,32,56,46,22,209,119,137,105,45,253,146,56,118,171,241,120,17,61,127, -44,233,52,154,41,152,122,169,90,112,60,87,227,230,184,254,51,77,127,69,42,166,254,72,210,51,232,176,164,37,244,11,205, -231,250,50,173,183,12,167,83,47,244,76,215,227,42,199,105,244,152,164,173,84,46,152,46,162,10,73,187,105,158,164,93,180, -73,112,108,86,237,43,224,225,255,174,41,219,107,166,214,83,137,241,127,139,56,134,250,232,126,226,216,107,208,45,210,15, -151,201,250,26,172,163,162,130,30,150,180,129,126,40,105,152,254,91,210,229,100,74,127,61,133,74,37,61,149,202,36,93,79, -115,36,29,164,213,146,14,208,70,233,151,167,75,125,1,61,46,166,255,33,105,51,253,90,210,16,189,162,249,211,164,252,106, -154,46,233,42,234,23,138,63,168,233,26,233,207,61,82,95,173,214,87,171,245,213,106,61,181,186,93,173,110,87,171,219,213, -105,249,58,45,87,167,229,234,180,92,157,150,155,77,75,165,254,217,200,58,44,249,220,65,182,166,14,73,219,201,41,233,124, -114,105,90,160,249,94,77,75,36,109,35,159,166,51,228,190,234,149,122,235,209,255,231,37,173,163,135,36,117,208,15,72,157, -115,143,75,58,151,186,244,126,114,202,253,165,230,215,0,143,184,71,210,153,116,175,164,106,125,26,224,23,223,151,116,54, -61,33,233,70,250,137,164,97,122,82,211,167,36,45,165,167,181,220,51,146,214,211,79,37,157,69,63,151,180,147,220,178,223, -211,168,80,83,143,80,252,34,73,23,83,177,80,251,191,82,210,25,52,75,210,10,170,146,116,45,213,75,218,66,13,66,197,139, -249,146,14,80,88,198,133,38,57,159,57,200,176,190,166,227,194,111,101,60,152,11,11,40,234,148,116,58,125,91,210,74,250, -14,241,121,126,138,228,183,106,249,86,120,236,135,4,159,219,74,190,77,219,167,13,30,253,8,241,249,172,244,183,195,206,47, -19,231,141,253,82,174,3,30,206,126,63,95,183,155,15,185,253,250,249,122,253,252,255,36,157,67,127,212,207,29,66,229,156, -43,37,29,162,33,193,249,103,35,93,73,156,131,42,61,11,117,251,133,144,191,73,210,26,217,207,66,100,183,127,146,52,64,237, -66,241,89,223,105,186,221,105,186,255,211,116,63,167,233,126,78,211,253,116,98,252,191,33,166,200,136,4,231,23,106,92, -139,53,237,210,122,186,144,205,158,46,56,247,85,207,221,218,191,248,12,2,91,190,247,32,185,255,113,94,115,162,141,36,247, -189,53,42,199,18,142,92,142,196,245,46,156,105,11,215,170,231,128,110,207,252,167,78,85,180,2,116,173,174,175,213,245, -109,121,245,109,160,151,233,250,217,90,175,157,167,127,37,234,191,170,235,235,53,191,59,175,254,67,168,127,81,215,55,104, -253,211,129,195,90,255,14,80,207,58,85,63,71,183,203,31,255,221,168,63,160,235,27,243,198,151,169,127,8,245,55,233,122, -206,175,127,129,196,255,217,144,146,251,131,166,127,11,229,234,10,215,228,202,229,107,84,125,93,30,239,84,93,94,8,186,36, -175,188,114,141,202,211,89,102,8,229,115,116,219,152,166,231,107,250,9,77,111,210,244,94,77,127,156,215,199,111,53,239, -101,169,211,144,229,111,14,168,187,195,132,183,8,207,117,240,153,9,47,251,238,176,215,66,84,31,246,26,52,236,51,112,46, -177,60,235,121,124,64,229,254,97,212,156,231,189,148,248,212,139,7,118,96,141,221,50,223,183,180,220,127,15,168,123,193, -121,178,23,143,136,7,12,236,35,200,122,109,249,204,113,205,68,29,203,254,102,64,157,105,225,128,69,225,90,11,50,95,66, -141,91,204,46,89,6,221,55,99,124,30,248,224,50,41,99,203,83,30,119,121,180,153,1,154,244,222,134,62,61,34,233,189,133, -219,24,157,70,17,120,183,162,204,109,60,228,243,197,219,22,193,131,130,175,23,235,145,17,189,51,160,236,192,119,21,135, -156,25,158,151,171,187,160,175,100,94,153,77,190,218,142,178,18,140,163,4,253,121,176,111,10,41,220,206,227,226,155,145, -199,136,7,110,130,207,250,122,59,202,106,16,191,166,83,165,177,147,206,11,180,131,151,107,225,59,166,197,23,101,173,165, -109,209,141,8,90,44,231,194,125,7,150,171,187,74,190,173,122,161,101,17,250,85,250,191,161,245,251,196,52,17,110,87,150, -23,82,242,124,105,169,224,91,110,104,98,237,109,208,117,38,207,163,220,231,208,250,160,199,77,149,22,244,216,69,82,79,24, -125,199,229,133,209,35,22,137,76,157,71,215,5,255,212,89,176,128,234,12,55,60,129,109,86,105,89,232,175,141,173,108,197, -3,94,196,226,58,179,8,117,62,88,46,30,40,67,6,204,252,233,184,181,122,44,95,3,151,194,52,59,189,20,61,76,67,107,143,189, -198,182,28,231,121,175,83,237,189,165,104,229,177,227,75,11,169,247,227,193,111,199,3,30,220,104,131,223,164,172,127,141, -46,87,247,204,169,254,245,111,240,175,98,228,97,14,57,163,241,229,234,110,57,225,109,65,155,225,217,5,52,92,239,160,225, -6,55,109,158,227,130,229,207,14,56,229,218,218,210,191,4,93,180,92,197,16,159,25,238,117,80,167,112,18,211,184,247,84, -212,133,123,11,192,41,144,52,220,231,70,95,31,133,157,135,251,161,179,223,1,45,69,122,5,124,122,5,130,71,84,28,98,221,66, -52,227,248,18,114,76,215,162,15,142,153,113,126,193,69,9,239,229,218,191,212,46,35,186,113,185,218,135,62,88,69,104,222, -45,203,115,126,88,140,185,241,93,251,206,229,106,223,44,41,244,208,208,69,46,114,238,115,126,78,220,34,238,181,190,191, -203,181,84,203,90,250,182,254,64,94,123,67,239,165,239,47,87,113,46,236,45,80,94,232,197,140,33,177,209,235,148,126,192, -207,241,64,19,246,148,207,123,182,215,57,165,237,147,31,208,182,51,219,182,153,219,82,166,45,143,133,199,112,88,175,219, -132,247,0,71,15,225,161,97,163,144,134,225,41,197,217,117,56,146,183,14,133,122,29,10,97,177,217,114,29,60,122,29,60,88, -135,162,236,58,64,79,127,225,191,176,14,239,101,215,97,229,9,215,193,94,161,215,1,30,228,212,163,47,4,175,148,231,221, -174,71,5,26,95,138,44,43,154,235,183,79,232,126,223,86,239,71,116,191,238,204,90,214,173,200,173,69,134,23,204,227,153, -50,218,17,181,172,80,239,84,134,197,52,216,138,45,54,108,20,203,248,170,222,204,44,206,107,147,89,231,101,39,224,173,95, -145,31,195,44,57,167,179,87,168,56,234,11,116,216,211,176,190,241,64,1,180,134,3,188,219,93,188,239,16,47,46,144,183,140, -156,158,115,79,160,123,247,9,120,151,156,128,247,233,99,230,199,255,174,63,1,239,214,60,158,45,45,71,244,181,21,188,179, -217,14,165,176,195,157,210,14,56,111,204,18,26,182,120,132,150,180,162,73,15,173,80,239,100,170,140,122,170,54,124,98, -184,221,135,85,253,50,106,224,143,237,94,172,215,52,73,227,240,71,161,75,28,37,88,210,139,231,18,72,184,37,141,123,43,52, -191,132,252,6,238,96,194,111,52,138,34,17,124,155,103,51,19,117,53,114,124,166,204,23,28,210,103,231,94,216,220,52,87, -151,13,250,245,10,149,59,86,153,24,139,25,222,0,221,198,108,98,26,247,206,228,88,39,226,222,58,121,10,249,230,119,244, -205,0,183,150,163,180,81,105,125,22,254,220,138,200,201,103,146,137,189,228,202,158,12,126,179,4,168,196,242,5,255,94, -132,82,163,161,206,251,57,104,217,172,125,73,32,223,205,248,239,187,43,84,125,216,235,149,107,109,104,111,19,43,51,231, -178,26,141,140,155,94,245,214,78,157,203,106,15,58,87,242,253,14,115,16,152,131,8,183,249,56,39,167,112,27,230,130,252, -142,159,195,243,160,33,144,70,196,245,11,246,127,191,104,36,213,103,169,236,171,12,251,70,157,193,190,149,234,125,166, -207,154,240,54,192,114,195,225,50,26,222,84,6,139,148,81,165,249,22,180,148,227,230,227,49,106,140,114,26,222,80,14,62, -110,154,56,87,42,13,236,40,115,187,220,201,243,176,214,117,198,66,248,192,65,142,229,27,102,226,105,62,158,62,45,159,42, -166,212,85,78,121,154,46,245,197,189,115,216,242,84,107,250,140,5,243,220,124,15,199,105,155,68,28,59,108,24,88,91,41, -211,54,151,54,88,193,31,187,244,25,115,250,74,149,43,134,71,42,208,254,115,188,51,44,206,31,44,114,155,157,102,155,204, -31,44,57,110,63,133,17,176,106,76,175,180,169,153,91,97,211,183,160,99,232,247,71,245,10,155,149,182,90,225,161,236,10, -255,226,168,94,97,51,30,248,4,206,72,214,252,228,209,18,195,103,4,255,225,200,156,117,43,213,187,234,112,95,37,244,223, -192,243,48,146,222,219,53,189,5,148,91,205,146,227,49,164,230,86,10,247,67,54,112,189,28,75,13,214,48,238,253,136,28,1, -247,50,36,229,95,56,90,34,124,34,248,15,142,51,202,123,46,92,169,222,123,87,217,13,84,109,135,211,60,235,107,120,182,214, -236,190,62,142,243,105,101,7,204,217,33,100,38,229,64,93,167,53,67,246,236,128,246,26,211,79,135,17,239,194,184,194,212, -88,202,26,124,158,175,177,12,33,68,240,247,126,187,196,40,178,252,118,163,197,126,209,34,123,109,37,151,244,19,131,238, -91,169,238,62,170,255,9,111,140,117,90,195,163,51,200,111,215,153,106,181,77,140,35,222,118,30,237,194,220,59,141,82,204, -113,2,123,128,51,139,171,112,235,58,12,7,207,212,6,223,80,61,241,28,76,158,67,47,103,137,215,194,59,61,86,141,133,44,81, -142,129,163,65,67,118,255,240,205,88,237,148,63,106,155,87,25,24,139,17,238,85,51,103,11,242,188,161,79,116,138,10,57, -111,83,90,220,47,111,156,53,66,205,89,230,92,222,26,153,115,117,156,113,228,168,223,224,88,226,163,224,123,42,154,168, -125,210,40,123,10,18,231,130,220,175,99,16,209,141,251,116,163,79,119,167,183,140,124,115,121,54,55,20,121,176,94,107,41, -252,240,76,204,224,11,200,143,185,119,39,44,225,119,151,144,111,102,240,213,13,237,179,104,34,16,199,125,212,227,236,116, -46,162,240,46,140,197,129,168,231,232,128,84,39,124,113,67,123,53,218,206,66,46,204,118,43,64,6,31,32,231,195,214,11,187, -28,242,77,39,191,107,129,246,58,171,3,122,62,137,53,139,183,253,59,181,91,126,119,240,41,140,216,221,40,84,251,74,110, -239,234,116,189,116,180,14,103,220,196,210,21,244,80,103,240,121,191,27,51,123,144,228,157,186,31,51,226,239,56,84,246, -178,66,250,22,123,241,126,204,141,223,253,249,28,225,20,118,90,224,20,248,122,149,133,121,90,225,212,116,216,236,243,124, -2,165,148,127,155,210,202,183,203,12,144,173,109,75,127,45,151,214,182,165,63,183,42,89,248,119,169,244,58,246,239,45, -144,15,190,84,100,250,173,70,211,103,14,163,222,15,221,51,143,213,152,183,131,43,243,118,240,28,146,178,208,56,91,106, -236,64,187,79,201,118,53,102,29,228,214,178,246,223,13,239,154,9,185,19,69,131,186,140,46,229,11,166,160,78,68,3,166,53, -166,37,79,29,51,239,201,33,159,50,17,163,130,117,255,82,233,253,12,202,156,141,112,214,196,247,232,185,176,223,124,82, -119,32,129,155,165,186,11,24,244,237,65,229,159,190,64,149,128,21,225,109,87,242,72,140,69,70,0,43,184,13,235,192,235, -140,27,138,215,207,39,223,188,233,210,115,250,228,205,199,137,88,29,252,139,138,216,19,129,237,90,214,98,238,31,252,194, -39,115,38,142,73,179,208,99,128,248,62,207,125,87,203,236,129,253,246,233,65,149,87,251,106,217,59,125,38,223,99,156,114, -39,26,242,198,198,239,106,130,239,229,242,197,231,7,149,111,248,2,19,129,115,229,141,171,36,91,119,36,83,231,205,213,101, -250,121,53,211,79,201,7,247,227,211,54,249,7,228,183,18,199,179,18,218,232,170,162,78,215,90,220,111,212,110,114,33,159, -9,23,206,160,218,7,125,46,113,249,130,31,174,194,9,80,88,224,51,244,46,117,113,155,13,69,179,168,227,240,34,172,2,191, -233,246,20,213,254,198,231,90,112,164,157,150,91,69,174,184,215,207,246,117,116,236,154,131,250,106,185,234,178,205,180, -106,234,248,115,21,120,85,188,250,54,175,3,252,21,123,164,56,115,174,187,42,11,222,147,81,255,32,234,107,236,118,222,181, -182,175,49,120,207,97,151,75,4,159,58,236,42,16,226,242,224,127,250,221,149,72,84,131,127,46,114,97,191,185,56,227,219, -128,214,31,201,198,166,45,176,133,41,215,37,182,74,125,247,232,43,245,89,62,17,126,12,179,218,35,150,47,184,108,30,45,39, -167,131,71,143,243,164,160,246,11,98,96,193,173,245,180,220,40,176,121,244,56,37,10,59,126,57,147,58,158,155,78,117,246, -156,204,169,239,232,88,82,40,103,132,122,107,98,233,199,40,218,239,28,42,49,69,127,240,47,135,17,179,15,219,182,8,254, -250,176,141,128,191,41,248,164,207,12,254,89,221,221,121,28,95,90,165,214,135,207,58,206,179,124,237,29,94,19,126,27,198, -121,238,19,241,165,65,106,243,5,223,225,247,202,134,204,151,238,130,252,77,28,223,138,176,34,69,105,49,147,119,175,139, -199,225,194,56,124,78,95,1,223,202,194,158,74,220,232,246,115,236,47,102,159,125,133,220,158,69,158,115,100,47,144,243, -248,122,58,94,105,67,212,99,235,186,200,227,169,44,86,103,234,43,210,186,56,83,109,117,131,70,164,176,148,62,229,251,33, -180,237,244,116,83,142,119,61,120,30,103,141,211,202,227,221,8,94,93,97,189,228,184,176,23,92,56,99,38,206,60,135,10,107, -143,27,27,34,228,43,133,117,197,93,56,151,14,97,214,157,5,109,84,234,121,202,121,209,67,182,151,243,67,236,236,165,119, -208,119,188,254,162,226,204,120,60,172,131,119,231,67,228,113,119,186,225,145,119,33,211,122,168,6,214,116,34,110,187, -100,156,112,202,248,224,164,52,206,151,82,242,23,5,159,43,242,248,139,26,61,165,158,81,10,62,93,228,9,190,205,123,99,18, -30,193,223,101,85,235,239,84,84,222,118,80,92,216,124,163,56,40,56,199,51,100,94,221,182,90,125,231,85,229,132,205,157, -124,54,185,113,122,179,205,113,118,27,225,107,213,124,12,94,7,216,232,50,172,67,167,3,209,245,90,196,162,192,103,233,54, -60,47,114,52,208,84,185,235,33,231,113,212,56,56,234,70,228,185,63,181,254,70,212,179,134,58,151,159,38,218,230,209,33, -147,79,231,203,200,239,44,38,61,2,139,87,143,243,136,74,151,90,189,203,100,92,199,234,137,42,105,45,33,243,82,73,29,108, -177,24,86,183,211,134,111,13,169,24,218,105,186,116,84,85,209,148,163,168,155,130,79,22,57,130,79,20,57,252,206,70,135, -58,99,199,97,247,9,29,63,119,203,189,132,8,122,97,115,122,23,114,132,66,25,53,4,237,131,141,62,204,54,194,76,171,165,109, -220,228,182,249,60,61,75,190,163,89,147,141,213,97,129,222,133,69,190,50,223,244,14,36,57,62,43,124,141,58,97,108,121,86, -221,174,169,60,179,176,162,175,30,213,103,150,60,97,54,52,206,34,173,221,213,177,255,213,163,178,45,172,217,32,61,87,157, -56,182,44,171,19,7,209,187,44,248,227,78,225,210,183,22,117,99,9,95,195,107,115,53,106,253,14,228,221,182,223,209,104, -171,185,158,37,227,196,230,236,251,171,71,86,79,189,251,241,61,248,71,171,179,241,118,253,94,154,23,246,200,236,195,144, -117,63,93,173,238,156,252,190,204,103,76,108,216,75,253,94,174,47,144,251,29,247,154,213,234,183,12,136,59,6,175,141,75, -174,17,191,153,230,44,208,164,105,134,58,209,249,61,159,19,22,236,180,113,210,88,193,183,112,95,177,26,141,112,114,58, -233,86,182,207,193,187,103,131,35,156,44,3,111,134,204,100,125,211,235,28,179,225,47,67,180,203,25,95,138,19,49,234,65, -150,131,21,207,107,39,91,9,191,37,230,5,31,231,59,234,126,10,254,77,189,151,116,99,166,252,93,231,169,210,6,21,217,24, -229,8,169,123,126,38,38,53,34,38,169,59,167,178,210,180,144,178,71,216,59,83,238,126,126,127,164,252,194,162,138,144,250, -189,5,207,209,45,119,6,71,55,181,147,194,215,170,168,114,155,228,227,206,114,173,138,40,183,201,181,134,127,26,106,213, -12,185,106,134,174,191,17,245,124,226,222,33,189,25,153,172,119,43,123,138,204,94,236,236,46,163,236,46,98,207,135,44, -172,11,223,235,159,114,195,112,196,3,147,216,21,42,123,240,59,131,247,40,175,47,18,149,14,220,154,92,165,242,214,116,25, -113,188,230,179,100,25,172,178,86,223,181,207,208,241,226,76,157,255,26,52,116,97,243,166,161,236,59,154,177,80,254,59, -154,179,196,44,58,219,168,162,179,204,106,249,246,73,221,48,63,26,82,239,203,125,98,17,98,238,52,228,31,87,200,76,138,41, -124,216,236,152,247,246,81,231,250,26,228,230,27,250,170,105,3,218,118,204,123,245,232,198,190,42,218,104,86,161,124,228, -232,134,190,89,224,227,196,157,247,252,81,95,73,240,183,153,247,112,68,159,12,169,239,10,234,112,55,216,208,59,139,158, -246,237,3,173,166,82,115,31,45,104,47,149,229,187,107,39,2,31,231,24,235,189,140,87,223,216,216,139,243,30,59,197,247, -198,55,106,167,137,82,113,33,5,95,131,214,191,169,219,48,209,23,245,58,251,96,179,26,192,143,185,100,222,5,29,10,169,124, -71,205,183,88,199,82,131,190,18,210,239,161,68,238,13,169,73,37,66,191,19,69,206,244,238,209,42,163,17,247,131,173,194, -79,139,4,178,106,81,131,53,235,64,134,30,3,199,47,249,193,35,153,156,191,68,234,16,250,123,12,33,179,43,83,247,245,88,72, -125,127,82,73,234,62,108,200,222,44,249,93,109,21,242,180,106,177,21,245,139,136,51,247,6,244,49,138,182,60,19,191,228,7, -95,205,220,191,139,116,31,213,217,62,170,228,111,79,229,221,95,219,130,231,250,81,159,250,254,231,82,208,43,179,191,110, -85,255,14,234,231,140,60,255,182,233,22,240,238,58,134,63,168,249,247,31,211,254,209,99,158,159,242,169,62,51,191,41,226, -119,135,207,250,212,111,122,94,247,169,239,73,254,234,83,191,91,56,226,83,191,105,98,189,219,88,184,84,253,94,5,14,77, -222,210,169,122,171,142,121,150,223,149,104,58,67,83,254,237,140,33,105,187,236,127,26,245,200,88,199,117,85,121,115,17, -164,114,25,67,63,153,154,246,80,238,119,81,153,239,118,12,73,219,228,243,28,205,239,207,202,121,116,91,245,11,15,213,215, -233,217,246,66,219,65,97,134,140,89,220,46,99,159,204,239,171,20,207,161,121,14,201,83,101,103,86,87,129,166,94,77,125, -90,198,167,245,10,101,62,202,124,167,101,72,58,139,50,223,177,177,44,127,199,59,71,247,203,223,171,206,209,123,176,1,255, -89,154,122,117,92,88,164,219,44,210,119,19,150,235,214,123,168,71,215,157,174,199,111,101,203,114,214,65,50,131,131,184, -203,204,165,250,182,142,190,206,182,129,5,189,205,3,203,6,58,155,231,247,117,116,52,247,158,182,160,189,121,97,255,64, -199,252,129,254,249,253,167,181,193,180,200,211,186,71,198,98,241,88,186,135,28,221,138,26,61,93,100,245,116,205,221,196, -159,40,123,251,198,38,163,233,68,34,189,99,77,36,30,217,30,77,210,226,99,57,129,104,50,153,72,46,14,140,36,38,199,70,3, -241,68,58,176,61,154,14,100,165,2,161,129,64,106,36,18,143,163,237,233,255,92,219,209,232,182,200,228,88,190,142,200,104, -100,34,13,5,149,203,38,199,199,247,102,249,43,34,233,116,127,100,108,108,107,100,4,23,155,65,50,6,67,100,14,134,66,84,51, -184,46,48,176,103,36,58,145,142,37,226,129,221,59,98,99,209,192,200,88,34,21,139,111,15,76,36,146,105,106,24,92,247,126, -245,227,177,209,24,134,176,43,54,18,37,177,138,172,85,27,251,7,168,100,213,228,72,116,13,106,6,227,19,147,233,245,172, -194,151,97,173,155,76,103,120,158,12,79,62,149,101,158,134,38,39,184,215,150,157,145,93,17,18,33,50,66,131,100,134,6,229, -7,122,192,7,50,10,12,219,12,225,195,10,133,54,135,168,62,20,137,143,38,19,177,209,214,173,153,217,182,102,231,221,171, -204,209,69,179,63,72,106,153,156,67,23,213,126,144,16,155,176,139,230,158,76,36,99,229,46,106,61,169,232,142,72,50,50, -130,225,197,82,233,216,72,23,157,122,178,6,203,162,169,145,100,108,34,157,72,158,120,32,99,209,156,124,40,58,164,124,233, -196,115,135,40,215,231,70,251,62,250,88,104,121,108,12,131,172,239,155,140,141,141,178,190,19,153,105,138,232,7,138,108, -136,166,224,178,39,158,173,22,25,138,166,211,112,176,84,174,203,15,152,66,70,184,139,102,102,133,70,18,241,116,52,158, -110,237,103,186,7,157,213,100,171,198,163,163,177,72,43,187,110,43,59,92,102,233,155,62,88,96,48,190,45,81,207,174,202, -133,252,225,188,175,116,23,53,124,176,208,80,58,146,158,196,168,235,222,79,44,187,129,242,93,233,24,25,29,29,234,149,202, -220,106,158,118,178,6,235,226,170,201,186,137,104,60,58,26,130,7,70,165,175,4,78,210,240,3,230,158,219,221,249,235,127, -140,208,134,232,72,52,182,139,245,148,102,69,18,169,214,190,201,248,232,24,150,161,44,159,185,50,194,76,136,150,231,115, -215,71,146,35,209,177,141,147,177,209,46,242,101,43,38,211,177,177,214,80,98,251,113,188,245,145,88,50,175,175,44,175, -139,54,30,207,236,62,137,155,156,52,62,224,32,104,11,141,36,198,91,147,137,177,88,235,78,68,181,214,99,66,91,253,177,145, -189,139,218,79,210,226,184,136,218,69,243,254,201,38,249,107,210,244,79,182,81,210,161,147,72,231,172,146,245,193,247,61, -113,186,104,217,191,172,45,199,97,23,13,71,82,231,158,220,80,199,105,57,249,164,51,19,94,31,73,239,224,48,241,129,210, -188,89,71,35,99,187,98,231,182,34,180,38,176,129,113,40,182,14,196,245,129,216,63,22,73,97,67,251,79,32,51,200,145,88, -215,215,158,160,126,77,116,124,171,22,136,66,164,250,4,34,67,177,237,113,68,140,36,118,73,229,9,170,195,59,146,137,221, -104,58,61,196,103,103,107,44,209,154,119,112,119,81,137,98,143,69,226,219,91,245,56,74,243,88,131,136,147,210,94,190,60, -230,186,173,59,163,35,233,169,188,161,116,18,51,205,118,35,121,178,235,200,86,222,191,85,121,236,100,116,91,235,153,209, -200,185,27,162,219,162,201,104,28,73,66,245,7,213,242,230,151,213,114,55,246,38,147,145,189,28,150,50,61,77,229,242,121, -117,2,118,247,241,35,237,201,142,63,39,154,154,202,91,25,73,97,51,78,100,12,146,207,59,94,16,199,205,113,130,224,77,29, -253,32,14,193,136,60,166,167,229,113,229,116,188,199,48,186,168,227,24,78,247,73,207,206,158,169,122,101,247,37,121,140, -112,108,156,215,114,250,177,44,181,139,74,142,219,38,212,123,28,235,196,249,102,222,65,16,72,237,197,153,49,30,72,69,147, -50,1,244,29,191,97,201,147,191,187,168,33,255,180,110,233,239,13,133,250,122,251,87,111,9,159,181,126,96,203,154,222,112, -255,202,45,161,117,67,97,18,155,200,216,132,132,111,19,82,84,107,211,224,230,65,114,108,90,133,20,112,21,216,72,252,54, -33,35,180,54,113,74,104,111,146,92,112,228,7,75,135,84,37,202,54,127,174,82,4,105,228,166,205,36,144,57,66,153,129,148, -209,24,238,163,186,225,147,103,49,205,195,255,82,86,80,255,79,136,99,219,13,159,96,139,77,97,102,246,88,97,100,100,36, -154,74,45,31,139,108,79,145,27,153,226,100,100,76,166,203,206,76,150,111,70,70,71,249,105,52,9,57,242,232,222,7,227,163, -209,61,104,173,158,100,11,119,100,98,66,39,67,228,136,164,148,39,110,61,38,75,166,202,44,39,52,32,195,158,90,219,141,27, -7,151,145,111,235,113,153,101,158,134,140,35,149,229,56,217,105,167,242,228,182,232,235,66,193,214,116,175,30,181,107, -107,90,201,65,76,151,82,124,22,195,4,228,216,154,230,115,132,236,173,156,8,146,103,68,31,40,225,189,19,81,114,96,20,200, -4,168,120,100,74,30,77,246,200,88,52,146,100,146,72,69,201,137,92,48,14,27,83,161,46,72,133,46,206,16,35,177,120,74,178, -101,105,117,116,175,20,150,54,242,232,66,56,177,17,58,108,236,130,120,154,196,40,185,71,179,41,56,57,244,92,92,138,194, -70,153,210,40,21,101,74,74,65,225,104,214,1,82,153,186,140,201,220,234,81,230,41,5,163,177,36,134,136,136,13,118,44,149, -25,186,35,122,30,150,62,69,5,114,83,246,39,70,97,192,104,38,182,83,203,182,8,110,101,163,129,116,34,48,146,140,70,210, -209,192,214,201,49,125,29,84,186,3,219,146,137,241,64,198,77,92,219,98,241,200,88,236,252,40,213,162,52,154,91,168,229, -137,100,222,197,73,9,215,176,72,102,67,159,72,192,222,22,75,194,153,60,219,96,162,209,204,130,187,185,67,229,198,100,109, -103,131,23,240,167,50,134,137,72,66,110,124,100,84,84,114,89,57,235,113,151,232,89,185,186,227,195,214,116,174,156,152, -24,139,141,200,51,48,227,224,165,96,31,55,206,138,124,102,126,6,46,181,28,127,109,34,23,216,242,164,164,18,148,150,169, -155,118,102,167,20,72,150,92,254,226,108,81,45,175,59,251,156,34,39,202,210,223,230,162,176,114,114,156,35,56,246,46,142, -74,101,156,19,26,20,162,240,37,73,70,165,6,214,75,85,178,160,147,185,181,145,113,94,45,78,79,212,102,159,137,90,126,58, -206,84,41,242,31,95,37,245,100,234,107,142,175,87,89,99,70,128,117,51,247,216,145,162,106,134,174,90,150,117,97,140,74, -143,154,199,72,213,40,228,114,208,227,134,93,136,234,76,5,21,101,30,38,57,23,162,114,253,200,103,199,148,70,46,93,145,82, -45,146,137,137,104,50,29,195,104,166,225,113,67,116,60,145,142,102,2,10,24,67,242,152,210,145,76,14,76,6,143,162,29,242, -114,161,175,35,228,220,17,73,173,101,223,113,161,176,67,238,48,107,71,2,126,93,192,159,202,111,69,140,204,216,232,30,178, -99,114,24,86,140,151,197,142,201,69,47,136,101,223,118,20,198,82,89,75,241,67,191,218,196,81,88,37,150,26,24,159,72,239, -229,130,92,2,174,206,189,38,113,197,116,214,64,46,78,62,87,202,238,119,102,167,237,217,153,255,198,196,60,23,225,202,129, -15,206,71,220,99,9,68,70,37,230,28,215,155,195,226,211,135,220,227,217,229,161,146,241,227,118,80,241,248,148,213,163, -194,241,60,207,49,198,199,201,28,79,109,199,71,122,146,172,56,47,146,205,159,136,33,241,232,110,222,62,48,83,156,205,102, -38,182,238,36,71,98,219,182,20,134,227,75,196,251,34,233,145,29,185,140,37,69,229,216,158,83,194,52,158,226,219,97,148, -178,99,43,120,135,208,244,99,185,103,38,97,29,169,69,153,19,219,93,246,175,212,144,55,17,207,189,28,145,26,74,242,57,170, -117,81,66,95,122,225,192,232,185,56,49,229,14,204,125,230,63,47,139,142,69,246,130,61,45,195,102,215,218,149,47,167,226, -71,102,34,206,68,124,249,216,100,106,7,121,18,241,53,233,201,12,27,35,227,241,40,191,220,144,74,197,168,130,57,99,49,142, -2,114,92,253,137,241,9,196,107,200,162,165,76,63,100,60,207,60,41,11,194,184,200,157,226,210,94,218,153,83,203,248,132, -192,93,26,178,165,216,4,241,99,194,27,185,153,169,203,69,92,206,249,154,159,31,167,220,41,207,140,165,119,228,246,88,77, -166,62,183,121,167,10,204,204,8,28,95,85,204,85,121,175,250,10,248,89,109,88,87,34,147,26,22,100,74,8,120,24,49,159,131, -137,92,19,59,177,155,67,112,233,4,124,242,216,89,85,158,128,57,148,142,78,132,119,39,168,124,74,93,46,50,145,53,193,25, -168,147,47,113,131,216,194,5,178,160,162,201,132,206,223,84,73,70,162,130,76,41,165,152,50,125,45,202,148,84,52,144,21, -50,148,20,103,74,225,196,114,132,6,50,121,103,207,72,70,183,243,75,149,228,212,55,51,228,72,74,47,34,183,162,42,98,168, -178,202,212,102,38,113,216,71,83,233,156,159,175,79,198,18,240,147,189,220,86,186,130,51,169,55,21,24,233,93,145,49,178, -146,236,87,102,114,50,78,37,169,108,254,170,95,158,81,105,42,47,239,206,48,157,153,55,205,174,212,200,142,232,40,18,6, -114,164,162,72,56,70,201,74,177,159,85,242,167,122,197,187,35,50,26,24,92,23,200,101,28,46,174,99,235,210,52,236,247,254, -252,164,172,16,12,246,218,53,28,66,139,249,65,231,144,147,177,81,84,238,224,235,4,246,13,38,106,165,56,5,177,83,242,161, -64,18,110,72,69,170,152,78,76,200,71,71,74,157,210,86,10,28,244,156,225,23,192,105,50,139,155,222,17,131,49,248,179,190, -13,21,184,234,160,209,248,4,57,211,9,121,223,163,233,147,241,19,185,210,204,99,216,121,14,83,49,25,127,159,21,180,97,241, -73,156,24,146,172,219,70,93,226,102,225,44,54,222,160,174,61,198,167,47,236,106,166,144,184,28,12,90,46,201,126,147,174, -17,22,255,191,111,197,116,171,16,100,89,223,52,22,143,56,139,143,154,116,191,81,180,217,38,186,92,136,175,179,252,39,133, -241,121,113,191,225,44,62,55,100,210,237,194,106,62,104,211,146,61,33,7,181,92,123,62,196,118,75,117,7,164,186,150,61,1, -58,67,252,192,112,54,65,244,83,194,108,49,252,187,141,109,213,33,83,92,37,10,90,174,104,217,108,26,223,54,10,175,219,108, -154,223,49,138,87,111,94,242,200,224,122,219,176,77,186,84,72,37,215,210,189,194,122,87,92,38,190,102,60,143,199,238,102, -252,235,166,223,10,114,86,135,182,172,222,219,220,108,236,170,246,155,244,13,209,66,15,129,89,220,221,77,143,49,165,183, -229,231,27,194,250,187,184,196,184,69,252,15,6,219,124,43,253,93,152,234,25,117,79,176,196,35,155,151,208,207,50,133,3, -134,169,186,82,29,209,125,198,9,186,249,140,161,186,57,200,148,14,25,89,133,107,182,24,23,84,215,40,161,27,101,229,205, -242,243,77,195,160,119,81,223,220,221,76,151,154,198,3,226,122,238,253,98,211,228,210,19,232,139,46,201,43,191,107,160, -252,132,177,15,229,37,171,175,163,143,115,213,109,170,234,178,188,242,229,92,126,79,149,15,112,249,91,134,44,239,231,14, -100,233,162,108,233,179,166,69,119,137,219,197,183,208,239,102,158,215,245,38,198,181,164,27,11,242,128,113,122,104,243, -112,207,218,115,122,154,109,50,246,118,57,136,30,144,149,161,152,41,254,191,40,221,251,136,92,196,230,115,108,178,197, -172,154,69,244,35,174,165,39,229,231,79,165,228,254,61,254,42,250,157,201,158,85,109,220,96,117,25,47,94,208,212,252,104, -200,40,222,13,155,237,217,179,103,111,12,238,35,250,149,190,197,61,182,160,95,216,114,105,133,207,107,25,111,139,154,222, -253,249,93,61,198,61,217,6,189,164,133,102,120,77,58,36,218,32,115,147,209,112,136,43,233,57,39,247,123,192,52,126,47, -250,177,40,47,8,33,156,54,153,2,133,71,44,147,53,10,195,22,14,18,133,54,57,68,141,109,54,75,141,119,88,226,110,182,106, -172,102,216,50,191,98,44,216,140,182,47,162,173,101,26,95,54,26,155,119,25,23,236,134,122,248,94,191,131,28,134,195,196, -136,143,138,1,122,205,41,238,192,64,150,8,223,52,139,88,249,115,1,155,102,213,156,66,15,88,230,139,226,57,241,10,87,118, -155,5,95,55,68,200,52,161,169,117,191,209,208,100,132,171,109,211,46,88,224,48,29,5,59,45,231,87,208,174,101,181,233,184, -93,148,180,192,69,222,16,179,91,118,154,198,205,198,204,102,12,149,70,108,3,91,231,182,118,116,111,218,14,219,105,156, -135,133,64,75,135,195,185,211,116,29,17,211,165,148,48,29,100,120,170,33,4,17,219,85,67,239,194,254,213,195,75,134,197, -140,105,166,128,141,254,203,22,139,187,107,248,201,248,147,104,132,121,109,163,133,141,140,29,90,211,27,218,115,254,229, -54,13,119,211,95,45,54,108,245,146,157,177,133,198,158,234,253,237,157,177,150,26,250,155,45,94,100,239,48,137,77,17,162, -27,45,233,191,242,243,18,199,212,186,59,156,226,16,236,184,215,116,98,202,75,238,149,139,231,55,141,55,133,184,237,54, -211,250,178,225,195,60,239,54,4,166,107,222,105,136,157,171,77,251,46,99,94,76,184,109,187,197,97,55,240,82,96,150,150, -109,219,14,99,164,218,118,46,112,8,88,219,226,201,26,23,116,161,194,97,148,175,98,41,246,54,167,113,183,241,186,112,54, -249,77,113,135,33,16,81,208,215,109,188,94,190,157,205,7,62,201,203,37,4,175,150,177,187,186,214,216,221,180,200,54,12, -239,42,68,160,79,58,48,42,103,241,5,85,244,176,67,60,37,247,146,233,66,91,12,237,144,16,126,211,249,128,176,107,204,130, -151,196,233,207,239,245,63,98,218,28,168,86,155,214,213,98,222,109,194,101,59,155,49,150,234,221,60,200,123,204,66,248, -221,189,162,196,107,23,250,141,209,106,12,212,190,213,114,191,195,235,185,179,165,251,244,165,182,123,161,154,12,175,137, -93,176,136,231,225,112,57,10,28,133,198,100,151,93,200,242,244,174,30,3,92,22,189,63,122,224,40,111,88,251,90,163,230,22, -219,244,35,24,162,103,140,237,17,83,168,97,16,134,177,223,178,101,31,33,135,217,210,221,39,29,195,14,181,74,227,89,116, -141,154,221,146,3,22,105,199,130,215,151,172,49,133,156,149,117,159,113,250,18,63,111,28,118,131,3,162,100,154,191,14, -158,208,221,51,140,230,176,188,191,10,6,122,208,161,98,195,31,45,131,157,24,165,63,89,114,153,233,74,27,195,112,54,169, -97,24,51,118,24,227,213,75,98,70,113,19,166,115,171,240,122,101,39,45,215,183,204,164,67,82,176,56,70,111,217,210,33,54, -211,87,212,158,53,130,59,140,225,234,37,7,100,44,178,209,241,217,182,1,47,128,29,121,114,176,64,55,142,139,213,150,245, -78,110,236,231,247,217,38,124,28,134,132,15,192,243,97,181,26,83,112,36,125,194,48,239,20,95,21,87,168,224,223,66,207,27, -226,98,195,89,221,49,243,190,102,147,190,100,52,210,47,57,200,98,115,154,244,59,209,246,13,155,186,17,195,110,118,97,103, -52,211,28,118,223,207,186,196,151,224,239,8,118,251,133,105,120,154,224,42,231,46,89,189,7,222,117,169,172,41,222,223,96, -156,95,173,56,70,80,252,220,168,104,52,230,26,151,10,171,224,61,81,94,104,156,10,78,149,85,254,225,114,187,124,168,220, -165,30,237,114,81,254,17,48,154,202,11,164,104,125,129,3,178,21,185,102,51,140,38,150,19,21,243,114,188,114,165,188,65, -113,12,112,138,115,197,29,57,185,109,198,41,220,214,168,168,175,152,157,233,110,88,246,94,159,235,159,25,103,148,47,207, -48,156,229,103,130,209,3,180,65,202,157,97,178,212,202,242,141,248,92,150,97,186,48,244,161,114,51,43,171,21,56,202,141, -60,150,28,139,27,99,137,169,177,56,42,26,42,230,84,212,86,4,42,106,42,234,68,185,37,76,81,96,86,26,248,39,140,142,125, -251,172,91,230,204,23,207,204,17,226,239,192,161,70,33,126,5,28,12,10,113,23,112,104,174,16,111,2,247,240,143,174,237, -181,23,185,72,28,3,108,98,151,67,255,230,70,208,146,139,246,89,135,79,53,47,54,168,71,28,104,18,214,43,77,134,184,178,89, -136,155,128,123,128,103,129,55,129,187,91,16,241,29,133,234,215,242,104,243,74,203,74,113,85,171,176,222,108,21,226,234, -54,244,11,188,2,236,107,39,67,184,139,13,113,85,96,8,98,87,183,135,197,61,237,66,252,0,248,21,240,58,112,203,60,33,238,7, -126,12,60,11,188,9,28,232,32,75,216,94,76,80,112,211,115,208,244,134,142,45,226,209,14,33,158,154,47,196,51,11,160,29, -184,122,33,57,157,165,101,74,76,255,55,10,217,103,23,26,198,193,78,97,220,180,200,48,14,44,6,186,76,227,234,238,66,227, -192,146,168,245,66,143,41,238,233,53,196,221,125,134,120,182,15,86,3,61,216,143,33,1,119,15,96,40,203,133,56,2,252,106, -16,163,15,9,113,120,13,250,2,14,172,53,196,161,181,224,175,67,27,224,224,122,88,192,152,37,248,223,199,208,231,85,27,46, -20,247,108,16,226,170,33,254,13,158,223,45,220,23,137,3,251,172,23,134,4,42,247,133,69,193,65,224,210,141,148,251,251,69, -249,191,225,201,252,109,62,254,109,74,230,239,243,241,239,82,50,127,163,143,127,151,18,32,245,119,250,248,183,57,153,191, -213,231,160,220,223,235,51,189,234,183,52,242,119,83,1,245,183,154,234,75,33,19,80,50,252,255,180,11,175,250,253,61,255, -127,232,70,64,245,203,127,223,207,212,242,252,255,75,91,1,245,187,27,254,127,170,237,128,26,31,255,127,240,164,245,200, -31,228,121,21,159,255,174,224,255,1,51,160,178,16,144,80,0,0}; +{31,139,8,8,116,138,97,92,0,3,74,97,118,97,68,101,120,66,121,116,101,67,111,100,101,46,100,101,120,0,149,124,9,124,219,197, +149,255,155,223,33,201,178,108,203,178,19,59,142,45,203,142,29,43,36,190,226,28,78,236,28,62,146,216,137,115,96,43,161,196,252,161, +138,173,36,10,182,228,88,114,14,216,46,161,7,9,45,44,148,35,77,41,165,180,92,225,40,176,20,10,109,129,210,66,91,216,101,129, +109,217,54,244,164,45,252,75,11,165,244,96,129,150,146,253,190,153,145,252,75,98,72,155,124,190,122,243,123,243,230,205,204,155, +55,111,222,252,164,100,56,182,207,219,212,178,144,238,255,216,139,247,126,127,96,206,252,159,157,251,212,223,22,175,248,196, +61,193,55,6,163,75,42,189,255,218,116,38,209,24,17,237,219,178,32,64,250,207,135,55,17,157,45,20,127,21,240,188,77,116,22,232, +43,46,162,16,232,187,94,162,123,152,230,18,229,128,166,11,137,174,95,78,116,13,52,188,93,79,244,87,224,239,128,209,64,100,3, +115,129,6,160,21,88,9,116,3,107,129,77,192,54,224,6,224,23,192,223,128,247,0,163,145,200,13,132,129,141,64,63,240,33,224,124,224, +34,224,114,224,38,224,86,224,14,224,30,224,126,224,235,192,163,192,227,192,83,192,31,128,226,38,162,197,192,54,224,106,224, +49,224,85,192,223,76,212,6,156,11,92,12,220,13,252,0,120,29,40,152,79,180,12,216,6,92,10,124,25,248,57,80,210,66,180,4,56,23,184, +24,56,12,220,5,124,7,120,1,248,61,96,44,128,237,128,79,1,143,2,127,2,66,11,137,18,192,253,192,111,128,105,139,136,22,1,91,129, +11,128,207,3,15,3,199,128,63,0,198,98,244,5,204,5,86,0,91,128,52,112,53,112,59,240,24,96,183,18,53,1,221,192,135,128,81,224, +98,224,48,112,23,240,8,240,44,96,45,65,127,64,24,88,1,244,3,215,2,183,3,15,2,63,7,126,9,188,12,252,14,120,3,120,27,120,23, +16,75,177,14,64,62,80,4,148,2,65,160,6,152,11,204,7,22,1,75,128,101,64,7,176,10,136,3,71,128,71,129,31,0,175,0,111,2,162,141,200, +11,20,0,165,192,108,160,5,88,1,172,1,206,6,38,128,75,129,207,1,255,14,60,14,60,3,28,3,126,7,252,9,120,7,112,183,67,15,80,9, +204,6,234,129,69,64,23,176,30,216,10,12,1,187,129,125,192,133,192,65,224,10,224,179,192,77,192,125,192,55,129,103,128,31,3,47,3, +127,4,222,1,172,101,208,15,172,0,122,129,17,224,114,224,26,224,75,192,221,192,253,192,55,129,39,128,231,128,159,1,239,0,211, +177,23,234,129,78,224,28,32,5,124,4,184,18,248,28,112,39,240,32,240,24,240,42,240,39,224,29,192,88,129,185,0,27,129,253,192,53, +192,93,192,3,192,183,129,255,6,126,13,252,30,120,23,200,91,137,249,3,97,96,49,176,18,88,11,108,2,6,129,115,129,33,32,14,140, +1,23,2,135,128,107,129,27,128,219,128,251,129,71,129,39,129,31,2,63,1,126,13,252,1,120,27,120,15,240,118,16,85,0,245,192,82,96, +57,208,5,244,0,27,128,205,192,121,64,12,216,5,140,3,151,0,95,0,30,0,158,6,94,6,222,2,188,157,68,51,128,57,192,74,224,76,96, +39,176,27,184,8,248,52,112,39,240,53,224,123,192,179,192,203,192,95,1,87,23,124,25,104,0,122,128,115,128,17,96,47,112,49,112,25, +112,53,240,5,224,86,224,27,192,143,128,55,128,255,5,222,5,220,221,68,133,192,76,96,46,176,16,232,6,54,0,231,2,49,96,23,240, +81,224,147,192,97,224,38,224,81,224,187,192,179,192,143,128,159,1,191,2,222,4,188,8,146,69,64,5,80,11,204,5,214,0,103,2,219,128, +36,112,17,112,25,112,21,240,89,224,102,224,43,192,55,128,111,1,255,9,252,16,248,41,240,18,240,7,224,109,192,88,13,123,1,11, +128,13,192,102,160,0,49,183,24,168,6,102,1,53,64,45,48,27,168,3,194,192,28,224,12,96,46,48,15,64,56,38,132,86,66,72,36,132,63,66, +152,35,132,52,66,200,34,132,40,66,88,34,132,30,66,104,33,132,13,194,246,39,108,89,194,86,35,108,7,130,91,19,92,142,176,132, +132,165,32,152,146,186,245,249,128,33,209,26,160,7,232,5,214,2,235,128,62,96,61,176,1,216,8,224,88,33,28,55,212,15,12,0,17,96,11, +240,33,96,16,248,127,192,121,124,254,0,219,128,97,32,6,108,7,70,128,127,1,46,2,14,0,23,3,31,5,62,6,124,156,148,77,50,127, +252,154,142,97,226,133,186,188,15,229,50,80,67,63,115,217,212,229,74,93,30,211,50,150,230,87,233,242,1,205,247,56,228,113,4,210,101, +154,159,171,249,51,129,60,224,26,205,207,119,244,85,224,40,7,28,242,197,90,158,203,165,142,182,101,142,190,202,245,216,88, +38,168,101,42,117,121,76,151,25,215,107,153,106,45,83,161,203,55,207,83,178,92,190,75,203,215,56,218,214,234,182,220,15,251,208, +67,122,12,13,142,113,54,58,198,214,228,24,27,151,31,155,167,242,2,46,63,57,111,146,159,177,103,179,67,79,179,99,252,92,126, +206,81,206,204,113,129,163,175,86,71,95,236,147,199,52,127,169,230,179,95,44,211,229,81,93,230,182,9,93,126,17,229,164,46,191,50, +79,229,52,92,254,11,202,187,117,217,194,230,216,167,203,62,148,199,117,185,20,229,148,46,135,80,222,163,203,243,80,222,171, +203,11,28,229,149,245,147,58,251,28,229,235,29,125,69,28,252,115,28,253,14,59,248,99,142,242,62,71,191,7,28,252,67,142,182,87,162, +188,63,211,151,67,254,40,202,23,232,242,189,142,182,207,57,198,195,107,151,145,127,210,193,31,115,148,31,118,244,245,4,202, +19,25,61,40,95,168,203,199,28,182,122,17,229,180,46,191,86,175,246,237,114,189,70,31,209,101,94,163,127,213,101,182,127,166,252, +152,131,159,241,159,14,221,150,203,157,14,127,232,114,248,67,183,230,207,212,229,107,164,207,55,209,131,164,232,10,193,109, +10,232,50,217,182,153,174,144,116,49,93,37,169,135,150,9,246,225,82,250,20,175,53,122,127,69,82,65,191,151,180,150,170,100,253, +108,154,43,56,46,20,75,185,42,205,175,210,252,89,250,153,233,38,193,123,204,162,207,16,83,63,253,69,82,85,95,163,235,107, +245,120,106,17,121,15,75,218,73,119,74,90,66,127,146,116,1,189,165,235,203,133,162,65,161,246,232,237,196,116,57,253,142,116,220, +23,28,251,43,233,211,92,134,228,155,196,177,206,67,79,72,106,210,119,37,181,233,199,196,177,206,77,95,144,180,154,30,209, +244,121,94,7,156,24,159,215,244,30,73,45,250,158,164,27,104,33,244,219,224,187,137,227,96,15,245,10,166,139,104,189,224,59,128,226, +123,179,212,75,71,36,205,161,85,168,247,105,61,121,186,62,15,156,35,146,230,82,151,80,180,91,112,140,204,163,111,17,211,42, +250,9,113,28,87,227,241,35,146,62,35,105,1,149,8,166,126,154,41,56,182,171,113,115,140,255,161,166,63,37,21,95,255,75,210,126,58, +38,105,33,189,160,249,92,95,172,245,22,227,148,90,9,61,211,244,184,74,112,42,61,37,105,19,77,19,76,151,210,116,73,151,81,179, +164,237,180,69,112,156,86,237,75,97,255,27,52,101,123,205,208,122,202,48,254,135,137,227,105,128,190,70,28,135,13,186,69,250, +225,42,89,207,126,167,168,160,199,37,173,165,255,148,116,51,125,95,210,53,36,164,191,206,165,66,73,231,81,64,210,51,169,70, +210,181,180,86,210,213,180,89,250,229,74,169,47,164,199,197,244,126,73,149,125,66,136,228,63,147,116,61,189,170,235,243,100,187, +62,42,146,116,29,117,10,197,239,209,180,79,250,245,10,169,183,74,235,173,210,122,171,180,222,42,173,175,74,183,175,210,237, +171,116,251,106,221,174,90,203,87,107,249,106,45,95,173,229,171,181,252,44,236,116,238,111,22,178,18,67,62,47,32,83,83,75,210,249, +100,75,186,144,92,154,186,53,63,95,211,2,73,155,201,175,105,177,220,111,157,82,111,13,250,191,78,210,106,250,182,164,46,250, +15,82,103,225,211,146,158,65,75,229,62,83,235,83,171,231,91,11,79,121,64,210,25,244,85,73,103,211,163,146,170,245,171,133,223, +60,41,233,22,122,86,210,205,244,156,166,255,45,105,17,253,64,210,26,250,31,73,103,210,143,36,93,66,30,217,95,43,229,104,234, +21,138,159,43,105,27,249,132,138,7,165,146,78,167,25,146,150,82,153,164,27,169,90,210,70,154,37,105,23,181,72,186,154,34,50,78, +212,203,121,204,70,230,117,175,142,19,191,144,241,225,12,204,92,81,183,164,211,232,155,146,150,209,99,196,103,253,92,201,111, +212,242,208,78,3,130,105,5,125,72,240,217,174,218,53,105,251,52,193,211,191,67,124,134,171,126,154,97,231,223,18,231,150,221, +82,174,5,158,207,251,97,129,110,183,0,114,135,244,243,245,250,249,70,73,235,232,53,253,60,95,168,60,96,141,164,17,234,23,156, +163,134,233,114,226,60,85,233,89,164,219,47,130,252,23,37,173,148,253,44,66,246,251,134,164,33,106,18,138,207,250,22,235,118, +139,117,255,139,117,63,139,117,63,139,117,63,173,24,255,207,137,105,144,222,35,206,59,212,184,150,106,218,166,245,180,33,219, +93,46,56,63,86,207,237,218,191,248,108,2,91,190,27,33,25,23,112,150,33,17,63,130,68,248,218,13,42,15,19,174,201,60,138,235,175, +68,253,51,27,212,115,72,183,103,254,135,231,41,122,35,234,127,167,235,171,116,125,147,163,254,33,212,207,222,168,234,103,105, +189,182,67,255,115,168,239,215,245,53,154,223,238,168,127,17,245,151,234,250,90,173,127,26,176,83,235,127,3,245,247,234,250,217, +186,157,115,252,43,33,23,222,164,158,235,28,227,203,212,111,66,125,167,174,231,28,252,195,184,24,236,88,175,228,198,53,189, +120,253,100,221,85,142,242,13,186,254,118,7,239,62,93,126,4,244,9,71,249,217,245,42,151,103,153,31,3,47,235,182,111,104,42,54,40, +26,208,180,78,211,118,77,7,52,29,222,48,217,215,94,205,251,232,6,214,109,200,242,185,171,213,61,99,204,159,135,231,106,248, +206,152,255,75,120,30,244,91,136,250,131,126,131,6,3,6,206,45,150,103,61,201,213,234,158,16,65,205,110,255,37,196,167,98,34,52, +130,181,246,202,187,129,165,229,246,172,86,119,136,221,178,23,159,72,132,12,236,39,200,250,109,249,204,231,129,137,58,150,253, +216,106,117,230,69,66,22,69,170,44,200,220,130,26,175,152,133,11,110,34,116,43,198,231,131,47,118,75,25,91,102,1,200,27,209, +102,58,219,220,127,59,250,244,137,113,255,109,220,198,104,53,242,192,59,138,50,183,241,81,32,144,104,90,2,79,10,191,145,175, +71,134,117,88,173,236,192,247,26,151,156,25,238,217,171,213,189,49,80,56,191,216,166,64,85,75,113,33,198,81,136,254,124,216,63, +185,20,105,230,113,241,45,202,103,36,66,55,193,119,3,29,45,197,149,136,95,211,168,204,56,159,118,135,154,193,155,108,17,56, +169,197,205,178,214,210,182,104,71,36,205,151,115,225,190,191,179,90,221,107,156,182,234,128,22,68,79,173,255,43,90,127,64,20,136, +72,179,178,188,144,146,255,34,45,21,126,211,11,77,172,253,127,86,171,119,156,129,146,128,75,235,131,30,47,149,89,208,99,231, +73,61,17,244,157,144,151,75,159,88,34,50,117,62,93,23,254,83,107,206,66,170,54,188,240,4,182,89,153,101,161,191,38,182,178,149, +8,249,113,6,85,155,121,168,11,192,114,137,80,49,178,101,230,79,195,45,215,103,5,106,185,20,161,89,233,149,232,161,0,173,125, +246,122,219,114,237,246,95,167,218,251,139,208,202,103,39,86,230,82,199,39,194,143,36,66,62,220,128,195,95,163,172,127,229,172, +81,119,210,19,253,235,34,248,87,62,242,52,151,242,249,53,234,30,58,230,111,64,155,193,89,57,52,88,227,162,193,90,47,109,157, +237,129,229,207,9,185,229,218,218,210,191,4,205,94,163,98,73,192,140,116,184,168,85,184,137,105,194,63,23,117,145,142,28,112,114, +36,141,116,122,209,215,191,194,206,131,93,208,217,229,130,150,60,189,2,1,189,2,225,87,84,60,98,221,66,212,227,248,18,114,76, +109,232,131,99,103,194,207,25,127,210,127,153,246,175,140,143,119,173,81,113,52,18,66,63,85,220,207,184,244,235,66,25,71,132,244, +203,222,53,106,175,6,96,57,161,121,27,215,76,250,106,62,230,207,119,247,205,107,212,222,90,150,235,163,129,139,61,228,62,224, +190,90,220,44,30,176,190,187,199,211,161,101,45,125,251,143,57,218,27,122,44,187,215,168,152,24,241,231,40,79,245,195,42,144, +216,236,119,75,95,225,231,68,104,30,198,23,240,159,227,119,159,208,246,130,15,104,219,154,109,91,207,109,41,211,150,199,194, +99,248,152,94,219,49,63,223,58,6,133,143,6,141,92,26,132,55,229,103,215,234,106,199,90,229,234,181,202,133,85,103,201,181,242, +233,181,242,97,173,242,178,107,5,61,93,185,255,196,90,29,205,174,85,239,148,107,117,111,118,173,208,79,85,222,148,107,245, +213,204,90,193,19,221,122,134,15,131,87,196,237,154,245,200,65,19,43,107,168,35,54,57,182,14,161,199,246,150,122,39,163,199,230, +205,172,247,15,28,235,149,225,189,224,224,153,114,148,200,103,214,168,247,56,131,162,0,246,100,171,14,26,249,50,78,171,183, +65,175,58,218,100,124,225,205,41,120,162,199,25,11,45,57,167,188,30,21,143,3,161,22,187,0,62,144,128,207,154,176,6,71,13,15,239, +95,196,157,143,200,219,204,164,158,242,158,83,117,135,167,224,45,152,130,183,178,231,196,249,241,159,190,41,120,91,28,60,91, +90,14,231,90,15,71,8,182,67,17,236,240,101,105,7,156,91,102,33,13,90,60,66,75,90,209,164,221,61,234,61,80,185,81,67,21,70,64,12, +54,7,176,242,119,163,6,62,219,236,199,122,21,72,154,128,207,10,93,226,104,195,146,126,60,23,66,194,43,105,194,95,170,249,133, +20,52,112,215,19,65,163,78,228,137,240,91,60,155,25,168,171,148,227,51,101,254,225,146,62,53,231,162,250,121,115,116,217,160, +127,235,81,57,105,185,137,177,152,145,126,232,54,102,17,211,132,127,6,199,76,145,240,87,203,211,44,176,160,165,115,58,184,85, +28,237,141,50,235,106,248,98,35,34,48,159,109,38,246,155,39,123,194,4,205,66,160,12,203,23,126,55,15,165,58,67,229,15,179,209, +178,94,251,146,192,253,43,227,191,119,245,168,250,136,223,159,245,107,174,185,175,39,115,190,171,209,200,248,235,87,111,10, +213,249,174,246,233,67,61,124,143,196,28,4,230,32,34,77,1,140,38,143,34,77,133,124,7,32,126,142,204,135,134,208,30,68,238,160,96, +255,15,138,58,82,125,22,201,190,138,177,111,212,89,254,120,143,122,135,26,176,198,252,181,176,220,96,164,152,6,183,20,195,34, +197,84,102,254,25,90,74,112,35,242,25,149,70,9,13,246,151,128,95,66,75,112,62,149,25,216,81,230,78,185,219,113,115,194,153,181, +8,62,240,57,62,19,250,103,224,105,1,158,174,146,79,165,39,212,149,157,240,52,77,234,75,248,103,179,229,169,202,12,24,11,231, +123,105,53,166,153,8,165,17,235,142,25,6,214,86,202,52,205,161,126,43,252,180,71,159,85,111,244,168,220,51,50,84,138,246,135,121, +103,88,156,135,88,228,53,91,205,38,153,135,88,114,220,65,138,32,168,85,154,126,105,83,115,114,133,205,192,194,150,129,223,28, +215,43,108,150,217,106,133,7,178,43,252,194,113,189,194,102,34,244,111,56,107,89,243,179,199,11,141,128,17,126,207,165,199,49, +173,87,189,31,143,116,150,65,255,23,120,30,198,184,255,14,77,111,3,229,86,51,229,120,12,169,185,145,34,93,144,13,221,32,199, +82,137,53,76,248,135,228,8,184,151,1,41,255,210,241,66,17,16,225,247,56,206,40,239,105,234,85,239,218,203,237,90,170,176,35,105, +158,245,17,158,173,53,171,179,147,207,130,180,178,3,230,236,18,50,35,115,161,174,213,154,46,123,118,65,123,165,25,164,99,136, +119,17,92,137,42,45,101,13,206,11,214,91,134,16,34,252,155,160,93,104,228,89,65,187,206,98,191,104,144,189,54,74,63,225,191,59, +122,213,59,250,114,11,253,195,87,70,57,215,50,7,7,166,83,208,154,92,237,68,232,124,138,241,76,212,138,152,60,14,83,142,35,40, +199,161,102,60,91,70,245,105,50,119,185,26,59,42,225,255,40,175,135,181,219,127,169,206,116,152,27,254,85,158,25,180,234,76,158, +39,91,113,86,19,103,164,159,101,43,98,46,200,72,77,30,39,71,140,197,217,61,182,136,92,122,55,93,165,215,165,220,192,120,141, +72,135,178,142,152,28,147,104,21,165,153,49,97,85,130,242,182,91,41,148,93,100,126,231,175,148,249,93,203,153,175,28,15,26,28, +111,2,20,254,187,138,56,106,47,213,201,158,194,196,121,39,247,123,103,175,250,14,162,220,139,62,189,173,254,98,10,204,72,52, +165,232,250,60,31,214,116,3,69,190,53,3,51,248,60,114,113,238,221,141,85,11,122,11,41,48,39,252,122,127,243,76,26,11,237,198,29, +216,231,110,117,47,161,200,30,140,197,133,200,232,106,129,84,43,252,181,191,185,2,109,103,34,239,246,33,207,206,193,109,33, +68,238,111,89,47,237,113,241,27,210,86,204,128,181,87,91,45,208,115,37,172,152,104,186,145,154,173,160,55,252,28,70,236,173,19, +170,125,25,183,247,180,122,126,123,188,26,231,224,24,130,255,99,173,225,95,5,189,152,217,195,36,239,243,93,152,17,127,247, +162,178,160,53,210,255,120,253,231,129,201,239,23,3,174,72,10,187,49,116,6,246,131,242,133,72,106,26,108,118,61,159,82,41,181,7, +212,202,223,33,179,77,182,182,45,125,186,68,90,219,150,30,208,168,100,177,7,138,228,106,242,30,216,6,249,240,111,213,154,7, +204,65,212,7,161,123,198,201,26,29,187,188,204,177,203,103,147,148,133,198,89,82,99,11,218,125,90,182,171,52,171,33,183,137,181, +255,122,112,207,12,200,77,21,49,170,51,186,148,47,152,130,90,17,49,152,86,154,150,60,153,76,199,147,75,62,101,162,74,41,235, +254,137,210,123,13,202,156,213,112,246,197,121,204,28,216,111,129,206,111,176,139,169,64,223,21,182,173,85,239,167,39,247,78,228, +135,211,169,106,71,192,20,11,23,78,180,96,175,185,236,132,63,196,209,199,83,117,41,184,11,22,94,83,131,200,232,65,30,31,148, +209,171,229,215,211,169,218,59,59,115,66,228,6,138,90,150,195,155,138,18,254,10,174,247,141,173,60,64,95,127,156,247,210,151,232, +152,105,9,177,32,252,139,128,25,254,227,49,211,22,98,97,248,153,66,195,214,123,251,208,90,181,87,2,161,114,129,21,133,231,95, +193,86,49,150,24,33,120,83,28,62,193,62,135,155,153,63,200,39,245,252,105,210,139,59,229,141,207,141,179,37,252,191,234,132,25, +11,237,210,178,22,115,95,13,138,128,204,3,121,190,51,49,38,158,77,142,180,67,69,54,167,253,252,90,229,119,1,255,88,40,33,111, +142,133,217,186,155,50,117,161,201,186,128,30,243,237,107,213,247,123,30,240,54,123,202,169,213,179,1,247,46,181,243,60,200,143, +34,185,176,230,195,1,143,184,116,225,83,107,97,183,220,156,128,161,119,180,135,219,244,231,205,164,150,99,75,136,109,12,205, +121,85,63,15,120,22,190,210,76,171,173,60,15,91,24,243,119,181,236,97,235,86,72,15,145,109,10,42,168,229,207,229,224,149,179,167, +216,108,39,248,54,246,83,126,38,79,240,148,229,252,77,158,34,71,80,95,105,55,243,14,183,3,117,225,251,143,121,60,34,252,220, +49,79,142,16,151,134,31,12,122,203,144,28,135,255,156,231,193,222,244,112,6,217,143,214,31,206,198,177,243,178,119,227,226,117, +234,59,56,142,172,93,50,42,201,40,230,184,115,23,56,238,220,179,101,44,133,55,24,45,253,111,28,103,107,241,25,98,105,159,155, +179,78,221,221,248,252,227,220,43,208,220,226,55,225,167,17,156,241,1,145,88,25,166,166,64,248,29,126,167,109,200,28,170,5,242, +55,114,60,203,131,85,243,210,98,6,239,86,15,91,209,3,235,4,220,129,28,190,241,69,124,101,184,45,126,146,207,152,124,246,139, +215,224,123,75,124,231,202,94,32,231,11,44,111,121,173,137,35,33,44,228,33,159,175,44,95,157,179,175,73,11,225,156,181,213,237, +28,145,193,82,250,148,127,245,161,109,171,175,157,38,121,55,128,231,115,87,186,45,7,239,139,224,85,231,214,72,142,7,254,230, +49,144,139,158,21,165,220,170,83,198,134,136,248,90,110,117,126,27,246,219,157,152,117,107,78,19,21,249,158,115,95,252,152,237, +231,156,17,59,121,229,93,244,168,63,152,151,159,25,143,143,117,240,14,120,140,124,222,86,47,188,234,46,65,238,199,42,97,77, +55,226,180,71,198,5,183,140,7,110,74,227,60,41,162,96,94,248,151,121,190,96,94,157,175,200,55,76,225,239,231,249,194,111,241,89, +49,129,21,226,239,215,216,155,14,103,115,185,35,226,162,250,27,196,17,193,121,159,33,115,237,7,215,169,239,225,202,221,176, +185,155,207,34,47,78,116,182,57,206,115,35,114,88,205,199,224,117,128,141,46,193,58,180,186,16,77,15,35,246,132,174,165,91,241, +188,196,85,75,39,202,221,0,57,159,171,210,197,81,118,88,230,2,39,214,127,17,245,172,161,218,19,164,177,166,249,116,212,228, +168,113,9,5,221,249,164,71,96,241,234,113,110,81,230,81,171,119,137,140,227,88,61,81,46,173,37,100,174,42,169,139,45,22,199,234, +182,218,240,173,1,21,51,91,77,143,142,162,42,122,114,212,244,82,248,217,60,87,248,191,242,92,65,119,157,75,157,169,156,87,140, +233,120,185,87,238,7,68,169,139,234,211,123,104,143,180,17,199,6,119,159,250,158,191,28,51,173,144,182,241,146,215,230,243,243, +108,249,254,103,125,54,54,71,4,122,23,22,5,138,3,211,90,144,248,4,172,200,181,234,68,177,229,217,116,135,166,242,140,194,138, +190,126,92,159,81,242,68,233,175,155,73,90,187,167,229,224,235,199,101,91,88,179,86,122,174,58,97,108,89,86,39,12,34,100,113, +248,233,86,225,209,55,25,117,139,137,92,203,107,243,25,212,6,93,200,197,237,160,171,206,86,115,61,91,238,245,173,217,119,99, +107,251,78,188,15,242,253,185,191,47,27,31,55,93,72,243,35,62,153,109,24,178,238,156,62,117,15,229,119,113,1,99,172,255,66,234, +242,115,125,142,220,239,6,13,247,169,223,84,4,138,56,246,241,174,225,53,226,111,45,57,35,51,169,192,80,39,56,191,67,116,195, +130,173,54,162,185,21,126,19,119,24,171,206,136,140,79,35,221,202,14,184,120,247,244,187,34,227,197,224,77,151,217,109,96,90,181, +107,22,252,101,11,237,113,39,86,226,4,140,249,144,213,96,197,29,237,100,43,17,180,196,252,240,127,240,189,245,32,133,255,170, +222,121,122,49,83,254,158,117,174,180,65,105,54,70,93,222,167,222,15,100,98,82,29,98,146,186,135,42,43,29,214,246,136,248,103, +200,221,207,239,166,148,95,88,244,133,62,245,219,15,158,163,87,238,12,142,110,106,39,69,14,171,168,114,171,228,227,30,115,88, +69,148,91,229,90,195,63,13,181,106,134,92,53,67,215,127,17,245,124,170,221,46,189,57,128,186,24,123,138,204,86,236,236,46,163, +236,46,98,207,135,44,172,11,223,235,58,225,214,225,74,132,246,98,87,168,108,33,232,14,223,175,188,62,79,148,185,112,147,242, +20,201,155,212,37,56,19,212,123,154,110,88,101,131,190,127,159,169,227,197,89,58,223,53,104,224,162,250,45,3,217,119,59,127,238, +115,190,219,57,91,204,164,115,140,114,58,219,172,144,111,173,212,173,211,92,175,222,201,7,196,18,196,220,2,156,35,151,203,204, +137,41,103,242,45,243,223,58,238,222,196,39,72,127,103,5,245,163,109,203,252,215,143,111,238,44,167,205,102,57,202,175,28,239, +239,156,9,62,78,205,249,191,58,30,40,12,255,34,243,142,15,231,197,122,245,125,68,53,238,11,253,29,51,233,251,129,3,160,21,84, +100,30,160,133,205,69,178,124,111,213,88,232,32,199,88,255,33,121,62,109,238,192,153,141,157,18,248,227,125,85,5,162,72,92,68, +225,63,64,235,95,213,13,25,103,227,122,237,247,176,89,37,16,196,92,50,239,144,26,215,171,156,66,205,55,95,199,82,131,22,172, +215,239,175,196,228,219,87,147,10,133,126,223,138,179,242,111,199,203,141,58,220,7,182,137,32,45,17,200,162,69,37,214,172,5,25, +121,28,156,160,228,135,95,201,228,248,133,82,135,208,223,149,8,153,193,152,186,175,13,235,213,119,52,101,164,238,200,134,236, +205,146,223,19,151,35,23,170,16,219,80,191,132,56,83,175,69,31,59,208,150,103,18,148,252,240,235,153,59,121,158,238,163,34,219, +71,185,252,13,172,252,158,65,219,130,231,122,99,64,125,199,116,20,244,222,236,175,108,213,159,135,245,115,70,158,127,99,245, +36,120,207,157,196,95,171,249,63,61,169,253,43,39,61,255,37,160,250,204,252,134,137,223,57,90,69,234,183,69,197,69,234,187,152, +242,34,245,155,9,31,232,78,173,55,14,90,131,231,243,65,231,21,169,223,126,44,0,109,47,58,81,127,207,73,207,130,244,247,89,164, +98,25,83,254,45,143,33,105,179,140,149,5,200,150,109,93,87,238,152,147,32,149,211,24,250,201,212,116,57,77,254,78,43,243,61, +146,33,105,147,124,158,173,249,93,89,57,159,110,171,126,101,162,250,90,145,109,47,247,80,22,211,101,236,50,180,141,76,135,173, +20,207,165,121,46,201,83,101,119,86,87,142,166,126,77,3,90,38,160,245,50,175,136,38,191,63,147,239,25,100,6,173,108,207,178, +252,125,242,108,221,47,127,151,59,91,239,197,90,252,181,52,245,235,248,176,68,183,89,162,239,36,44,215,174,247,210,114,93,183, +66,143,223,202,150,229,172,195,100,134,123,113,135,153,67,53,77,45,157,173,77,171,22,118,212,175,234,94,213,90,191,160,179,165, +165,190,99,241,194,230,250,69,93,171,90,22,172,234,90,208,181,184,9,166,69,190,214,62,52,18,79,196,211,203,201,213,174,168, +177,188,141,172,229,109,115,182,240,39,202,254,206,145,137,88,58,153,76,239,92,31,77,68,119,196,198,105,233,201,156,80,108,124, +60,57,190,52,52,148,156,24,25,14,37,146,233,208,142,88,58,148,149,10,245,173,10,165,134,162,137,4,218,174,248,199,218,14,199, +182,71,39,70,156,58,162,195,209,177,52,20,148,117,79,140,142,238,207,242,215,68,211,233,174,232,200,200,182,232,208,249,36,122, +201,232,237,35,179,183,175,143,42,123,55,134,86,237,27,138,141,165,227,73,4,243,157,241,145,88,104,104,36,153,138,39,118,132, +198,146,227,105,170,237,221,248,126,245,163,241,225,56,134,176,39,62,20,35,177,150,172,181,155,187,86,81,225,218,137,161,216, +122,212,244,38,198,38,210,155,88,69,32,195,218,56,145,206,240,124,25,158,124,42,206,60,13,76,140,113,175,13,187,162,123,162, +36,250,200,232,235,37,179,175,87,126,160,7,124,32,179,192,176,205,62,124,88,125,125,91,251,168,166,47,154,24,30,79,198,135,27, +183,101,102,219,152,157,119,135,50,71,27,205,250,32,169,110,57,135,54,170,250,32,33,54,97,27,205,57,157,72,198,202,109,212, +120,90,209,157,209,241,232,16,134,23,79,165,227,67,109,52,247,116,13,186,99,169,161,241,248,88,58,57,62,245,64,70,98,147,242,125, +177,1,229,75,83,207,29,162,92,63,57,218,247,209,199,66,171,227,35,24,100,77,231,68,124,100,152,245,77,101,166,19,68,63,80,164, +63,150,130,203,78,61,91,45,50,16,75,167,225,96,169,201,46,63,96,10,25,225,54,154,145,21,26,74,38,210,177,68,186,177,139,233, +62,116,86,153,173,26,141,13,199,163,141,236,186,141,236,112,153,165,159,247,193,2,189,137,237,201,26,118,85,46,56,135,243,190, +210,109,84,251,193,66,3,233,104,122,2,163,174,126,63,177,236,6,114,186,210,73,50,58,58,212,40,149,147,171,185,248,116,13,54, +38,84,147,141,99,177,68,108,184,15,30,24,147,190,18,58,77,195,15,152,251,228,238,118,174,255,73,66,253,177,161,88,124,15,235, +41,202,138,36,83,141,157,19,137,225,17,44,67,177,147,217,19,101,38,68,75,156,220,77,209,241,161,216,200,230,137,248,112,27,5, +178,21,19,233,248,72,99,95,114,199,41,188,77,209,248,184,163,175,44,175,141,54,159,202,108,63,141,155,156,54,62,224,32,104, +234,27,74,142,54,142,39,71,226,141,187,16,213,26,79,10,109,53,39,71,246,54,106,62,77,139,83,34,106,27,205,255,7,155,56,215,100, +222,63,216,70,73,247,157,70,122,210,42,89,31,124,223,19,167,141,186,255,105,109,147,28,118,209,72,52,117,254,233,13,117,138, +150,211,79,58,51,225,77,209,244,78,14,19,31,40,205,155,117,56,58,178,39,126,126,35,66,107,18,27,24,135,98,227,170,132,62,16,187, +70,162,41,108,232,224,20,50,189,28,137,117,125,213,20,245,235,99,163,219,180,64,12,34,21,83,136,12,196,119,36,16,49,198,177, +75,202,166,168,142,236,28,79,238,69,211,105,125,124,118,54,198,147,141,142,131,187,141,10,21,123,36,154,216,209,168,199,81,228, +96,245,34,78,74,123,5,28,204,141,219,118,197,134,210,39,242,6,210,227,152,105,182,27,201,147,93,71,183,241,254,45,119,176,199, +99,219,27,207,138,69,207,239,143,109,143,141,199,18,72,18,42,62,168,150,55,191,172,150,187,177,99,124,60,186,159,195,82,166, +167,19,185,109,212,53,21,187,253,159,89,237,229,124,232,77,169,228,148,233,46,207,26,97,82,52,117,34,175,39,154,194,142,30, +203,88,213,201,59,85,16,103,214,41,130,224,157,104,130,94,156,164,81,121,214,23,56,184,210,38,254,147,24,109,212,114,18,167,253, +180,7,240,242,19,245,202,238,11,29,140,72,124,148,29,98,218,201,44,181,21,11,79,217,107,212,113,10,107,234,164,213,113,154, +132,82,251,113,240,140,134,82,177,113,153,69,6,78,221,245,228,115,46,26,213,58,143,252,134,174,142,190,190,206,142,174,117,231, +69,206,222,180,234,188,245,29,145,174,158,243,250,54,14,68,72,108,33,99,11,178,198,45,200,115,173,45,189,91,123,201,181,101, +45,242,200,181,96,35,123,220,130,180,210,218,194,121,165,189,69,114,193,145,31,44,221,167,42,81,182,249,115,173,34,200,69,183, +108,37,129,244,19,202,12,228,157,198,96,39,85,15,158,62,21,170,31,252,167,82,139,154,127,64,28,123,119,112,138,125,122,2,51, +179,81,115,163,67,67,177,84,106,245,72,116,71,138,188,72,55,39,162,35,50,231,118,103,174,10,102,116,120,152,159,134,199,33,71, +62,221,123,111,98,56,182,15,173,213,147,108,225,141,142,141,233,140,138,92,209,148,242,196,109,39,165,218,84,150,229,244,173, +146,123,79,173,237,230,205,189,221,20,216,118,74,122,234,208,144,113,164,226,73,78,118,218,41,135,220,121,250,206,145,179,45, +221,161,71,237,217,150,86,114,16,211,165,20,31,232,48,1,185,182,165,249,48,34,123,27,103,147,228,27,210,167,82,100,255,88,140, +92,24,5,210,9,202,31,58,33,25,39,123,104,36,22,29,103,146,76,197,200,141,132,50,1,27,83,174,46,72,133,30,78,51,163,241,68,74, +178,101,105,93,108,191,20,150,54,242,233,66,36,185,25,58,108,236,130,68,154,196,48,121,135,179,121,60,185,244,92,60,138,194, +70,153,210,48,229,101,74,74,65,238,112,214,1,82,153,186,140,201,188,234,81,38,59,57,195,241,113,12,17,97,31,236,120,42,51,116, +87,108,55,150,62,69,57,114,83,118,37,135,97,192,88,230,128,160,134,237,81,92,237,134,67,233,100,104,104,60,22,77,199,66,219, +38,70,244,157,82,233,14,109,31,79,142,134,50,110,226,217,30,79,68,71,226,23,196,168,10,165,225,201,133,90,157,28,119,220,190,148, +112,37,139,100,54,244,84,2,246,246,248,56,156,201,183,29,38,26,206,44,184,151,59,84,110,76,214,14,54,120,14,127,42,99,152,136, +36,228,197,71,70,69,46,151,71,164,107,167,168,140,31,148,231,158,114,45,159,57,89,119,106,12,155,198,149,99,99,35,241,33,121, +170,102,188,189,8,236,83,6,93,234,100,58,115,122,169,229,212,139,24,121,192,150,103,47,21,162,212,173,238,238,153,109,147,35, +89,210,23,242,179,69,181,214,222,236,115,138,220,40,75,231,155,131,66,207,196,40,135,115,108,100,28,190,202,82,83,90,23,162,112, +44,73,134,165,6,214,75,213,40,240,1,121,138,49,54,68,71,153,217,219,157,162,186,83,101,100,22,122,138,96,248,84,65,149,123, +158,34,57,3,146,92,125,242,48,49,185,233,186,170,59,235,204,152,142,30,50,107,144,139,204,43,44,31,242,50,15,19,156,59,145,95,63, +242,49,193,205,186,165,189,149,63,72,209,241,228,88,108,60,29,71,63,5,120,236,143,141,38,211,177,76,208,0,99,64,30,69,58,90, +201,46,101,128,200,219,41,111,33,250,222,66,238,157,209,212,6,118,9,15,10,59,229,46,178,118,38,225,187,57,252,169,124,83,196,201, +140,15,239,35,43,206,102,182,227,114,17,115,226,217,247,33,185,241,84,118,242,252,208,165,118,104,12,19,141,167,86,141,142, +165,247,115,65,218,153,171,39,95,164,120,226,58,37,32,15,167,55,61,220,175,111,151,243,69,138,121,62,2,144,11,31,156,97,120,71, +146,136,117,42,144,187,71,181,135,91,124,158,144,119,52,107,102,42,28,61,101,27,228,143,158,176,10,148,59,234,8,196,198,232, +40,153,163,169,29,248,72,79,144,149,224,181,176,249,19,81,33,17,219,203,123,0,70,73,176,145,204,228,182,93,228,74,110,223,158, +194,112,2,201,68,103,52,61,180,115,50,7,73,81,9,246,216,9,129,23,79,137,29,176,68,241,201,21,236,230,52,237,100,238,89,227, +48,137,212,162,108,136,61,43,251,87,106,200,159,76,76,190,51,145,26,10,157,28,213,58,47,169,239,194,112,68,244,156,159,60,225,106, +204,125,58,159,187,99,35,209,253,96,23,100,216,236,72,123,156,114,42,8,100,38,226,78,38,86,143,76,164,118,146,47,153,88,159, +158,200,176,49,50,30,143,242,194,254,84,42,78,165,204,25,137,243,86,150,227,234,74,142,142,33,2,67,22,45,101,66,33,35,116,230, +73,89,16,198,69,54,148,144,246,210,174,155,234,230,152,143,43,54,100,139,224,242,137,147,98,20,121,153,169,203,121,92,158,116, +176,18,126,60,225,170,121,86,60,189,19,91,169,52,83,49,121,161,212,53,129,76,141,131,151,207,60,199,203,190,28,126,86,59,209, +147,204,228,117,57,153,18,2,20,6,199,135,88,114,178,137,157,220,203,33,179,104,12,238,119,242,4,202,166,96,14,164,99,99,145, +189,73,42,57,161,110,50,152,144,53,198,233,163,37,223,105,230,140,201,116,139,247,133,103,76,103,94,170,36,3,75,126,166,164,35, +150,172,145,217,103,94,166,164,54,186,172,144,81,34,63,83,138,36,87,227,172,35,123,76,206,214,228,45,60,125,60,182,131,223, +175,140,159,248,146,134,92,227,210,115,200,171,168,10,13,170,172,242,173,25,227,56,178,99,169,244,164,111,111,26,143,39,225,27, +251,185,173,92,126,247,184,222,72,96,164,247,68,71,200,26,103,95,50,199,39,18,84,152,202,102,161,250,61,26,21,165,28,217,115, +134,233,206,188,116,246,164,134,118,198,134,113,236,147,43,21,67,218,48,76,86,138,125,171,140,63,213,219,222,157,209,225,80,239, +198,208,100,222,224,225,58,54,51,21,96,143,119,57,83,171,92,48,216,83,215,115,144,204,231,7,157,9,78,196,135,81,185,147,47, +5,216,43,152,168,149,226,68,194,78,201,135,28,73,184,33,229,169,98,58,57,38,31,93,41,117,188,90,41,112,208,115,134,159,3,239,201, +172,114,122,103,28,198,224,207,154,38,84,224,194,130,70,163,99,228,78,39,229,173,141,60,233,164,206,41,166,77,36,166,242,174, +25,39,177,29,62,84,58,145,120,159,181,180,97,251,9,156,14,146,108,220,78,203,197,205,194,157,111,188,65,109,251,140,43,47,106, +171,167,141,226,147,96,80,175,36,7,77,58,44,44,254,71,96,249,116,155,16,100,89,15,25,75,135,221,249,199,77,122,208,200,219, +106,19,125,74,136,251,88,254,10,97,92,39,30,52,220,249,231,247,153,116,84,88,245,71,108,90,182,175,207,69,13,135,47,128,216,126, +169,238,144,84,215,176,47,68,17,241,61,195,61,15,162,87,8,179,193,168,216,107,236,168,232,51,197,167,69,78,195,39,27,182,154, +198,195,70,238,103,182,154,230,35,70,254,186,173,203,30,239,221,104,27,182,73,151,8,169,228,48,61,40,172,119,196,65,241,101,227, +87,120,108,175,199,159,118,250,181,32,119,197,134,15,175,219,95,95,111,76,84,84,154,244,21,209,64,223,6,51,191,189,157,14,25, +60,129,167,248,137,222,147,159,111,11,235,175,226,98,227,22,241,125,12,185,254,22,250,152,97,170,103,212,61,203,18,79,108,93, +70,47,100,10,215,26,166,234,80,117,71,79,24,83,116,118,131,161,58,123,65,118,118,139,252,124,96,82,237,134,243,140,11,51,162, +119,202,202,123,229,231,39,77,131,222,65,125,125,123,61,221,96,26,223,16,215,243,24,174,51,77,46,61,141,30,233,122,71,249,211, +92,126,218,120,15,50,203,214,125,134,110,228,199,219,84,213,151,28,229,163,92,254,155,42,223,202,229,175,27,178,124,51,119, +32,75,71,178,165,127,55,45,250,178,56,42,30,134,206,173,60,187,175,155,24,215,178,118,44,206,215,140,21,125,91,7,151,111,56,119, +121,189,77,198,190,54,23,209,11,178,178,47,110,138,151,69,209,254,199,229,130,214,159,107,147,45,102,86,46,161,223,113,45,189, +46,63,255,34,37,15,238,11,150,211,71,45,246,178,10,227,144,213,102,188,115,225,188,250,39,250,140,252,189,198,158,138,125,251, +246,237,143,163,27,209,165,244,45,93,110,11,122,219,150,203,44,2,126,203,120,83,84,118,28,116,118,245,61,238,201,54,232,144, +75,9,77,247,155,116,187,104,130,204,13,70,237,81,174,164,215,220,220,239,33,211,248,255,162,43,104,210,75,66,8,183,77,166,64,225, +113,203,100,141,194,176,133,139,68,174,77,46,81,105,155,245,82,227,147,150,184,27,230,88,22,175,28,180,140,59,141,133,131,162, +216,111,138,219,141,186,125,198,254,111,179,196,42,151,129,177,254,93,172,162,191,186,197,29,188,0,34,80,96,17,43,252,101,200, +166,153,149,103,208,79,44,243,118,241,75,241,42,87,182,155,57,247,24,162,207,52,161,162,241,160,81,51,207,216,92,97,155,118, +206,66,151,233,202,217,101,185,239,70,187,134,117,166,235,168,40,108,128,91,252,65,204,106,216,101,26,95,52,102,212,99,120,180, +205,54,176,117,110,109,198,140,76,219,101,187,141,221,48,62,90,186,92,238,93,166,231,55,98,154,148,18,166,139,12,95,5,132,32, +98,123,42,233,58,11,142,57,184,108,80,76,47,192,216,69,211,119,108,177,180,189,146,159,140,55,68,29,76,106,27,13,108,88,236,208, +202,142,190,125,23,92,106,211,96,59,253,208,45,167,142,121,223,107,44,220,122,216,36,216,251,126,158,123,32,126,203,158,37, +125,115,108,177,9,91,248,136,92,151,138,101,187,226,139,140,125,21,7,155,91,227,13,149,244,109,139,151,250,17,249,121,191,91,220, +10,53,251,77,55,38,190,236,1,185,108,65,211,248,147,16,183,222,106,90,208,134,217,222,109,8,76,218,60,106,136,93,235,76,251, +14,99,126,92,120,109,187,193,101,215,178,137,49,87,203,182,109,151,49,84,97,187,23,186,132,203,112,89,60,101,227,194,54,84,184, +140,146,181,44,5,63,251,181,219,184,219,184,157,29,160,184,192,164,219,140,186,243,49,192,235,92,232,209,157,127,97,57,61,227, +18,207,242,90,174,51,61,24,3,186,189,93,136,160,233,254,134,176,43,205,156,87,196,138,95,237,15,62,110,218,28,138,214,153,214, +181,98,254,173,194,99,187,235,209,79,249,94,30,192,253,102,46,188,233,1,81,232,183,115,131,198,112,5,6,97,223,98,121,223,226, +21,219,213,208,190,98,165,237,93,164,6,202,86,183,115,150,240,24,93,30,87,142,43,215,72,183,217,185,44,79,7,221,106,12,112,68, +244,254,196,161,227,188,13,237,195,70,240,102,219,12,26,219,43,208,51,198,246,184,41,212,48,8,195,56,104,217,178,143,62,151, +217,208,222,41,151,222,94,215,36,13,99,209,151,212,236,150,29,178,72,187,14,124,185,112,189,41,228,172,172,7,140,21,203,130,188, +29,120,161,15,137,194,130,96,53,214,186,125,249,32,154,195,170,193,114,24,232,41,151,218,241,151,219,6,187,41,74,87,217,130, +173,72,119,217,24,134,123,158,26,134,49,109,167,145,168,88,22,55,242,230,25,123,218,110,17,126,191,236,164,225,186,134,25,244, +61,41,152,31,167,107,92,114,177,183,210,211,106,187,26,225,157,198,96,197,178,67,7,57,174,216,232,248,28,219,192,10,195,142, +60,57,88,160,29,7,194,58,203,122,107,114,236,23,118,216,38,188,24,134,196,250,194,183,97,181,74,83,112,124,124,201,48,239,20,247, +136,79,233,240,78,111,27,226,128,225,174,104,153,241,213,122,147,110,52,234,232,143,28,58,177,253,76,196,253,166,251,108,106, +71,100,186,219,3,223,175,167,217,236,138,159,247,136,155,224,171,8,97,135,132,105,248,230,25,123,43,206,95,182,110,31,60,231, +10,89,147,127,176,214,184,160,66,113,140,176,248,145,81,90,103,204,49,62,33,172,156,191,137,146,92,99,46,56,229,86,201,185, +37,118,201,64,137,71,61,218,37,162,36,10,198,188,146,28,41,90,147,227,130,108,233,100,179,233,198,60,150,19,165,243,39,121,37,74, +121,173,226,24,224,228,79,22,227,147,114,59,140,51,184,173,81,90,83,58,43,211,221,57,178,247,154,201,254,153,113,102,201,234, +12,195,93,114,22,24,203,129,38,72,121,51,76,150,234,41,217,140,207,238,12,211,131,161,15,148,152,89,217,172,70,67,170,232,201, +48,92,96,76,202,200,193,121,49,184,93,106,112,174,210,218,210,217,165,85,165,161,210,202,210,106,81,98,9,83,228,152,101,6,254, +8,163,229,192,1,235,185,217,11,196,129,58,33,142,2,207,3,135,194,112,123,224,24,240,252,28,33,110,60,67,8,254,55,202,228,218, +116,177,135,196,20,32,219,200,113,233,223,225,8,90,118,241,1,235,227,243,172,143,26,200,81,238,159,39,172,35,245,66,60,84,111, +136,159,130,190,1,124,188,65,136,123,129,39,128,3,141,8,240,238,92,217,174,7,237,30,110,236,21,47,54,10,235,137,38,33,94,2,14, +53,11,113,61,240,34,240,151,102,50,132,55,223,16,87,134,182,64,244,208,252,179,196,209,249,66,60,12,60,7,188,4,28,105,17,226, +46,224,49,224,121,224,21,224,221,22,178,132,237,199,100,5,55,141,162,233,149,11,182,137,135,22,96,4,11,133,120,114,17,180,3,135, +22,147,219,29,40,86,98,250,239,14,200,62,191,216,48,46,91,34,140,107,150,154,198,187,75,133,241,110,155,105,220,181,204,107, +220,184,124,167,245,238,10,83,60,221,9,75,117,153,226,249,110,204,174,219,16,151,173,194,72,87,99,8,107,240,12,188,180,22,186, +215,163,143,13,224,3,87,110,52,196,189,27,193,223,4,75,156,9,235,158,9,11,24,51,5,255,57,32,208,225,145,129,139,49,169,1,76, +38,194,191,206,11,122,133,247,99,226,208,1,235,181,8,215,30,218,44,114,110,4,46,219,146,253,255,149,156,191,233,201,252,223,129, +252,91,149,204,255,31,200,191,83,201,252,31,130,252,59,149,16,169,255,71,144,127,171,147,249,191,4,93,52,249,255,9,154,126, +245,59,26,249,123,170,144,250,127,164,54,129,225,10,41,25,254,247,244,194,175,126,251,206,255,6,222,8,169,126,249,255,31,52,181, +60,255,27,109,43,164,126,151,196,255,142,219,14,169,241,241,191,193,39,173,135,255,77,62,255,152,71,254,155,184,77,68,255,7, +100,114,50,46,48,81,0,0,0,0}; #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ STATICMETHOD (getAndroidMidiDeviceManager, "getAndroidMidiDeviceManager", "(Landroid/content/Context;)Lcom/roli/juce/JuceMidiSupport$MidiDeviceManager;") \ - STATICMETHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "(Landroid/content/Context;)Lcom/roli/juce/JuceMidiSupport$BluetoothManager;") + STATICMETHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "(Landroid/content/Context;)Lcom/roli/juce/JuceMidiSupport$BluetoothManager;") DECLARE_JNI_CLASS_WITH_BYTECODE (JuceMidiSupport, "com/roli/juce/JuceMidiSupport", 23, javaMidiByteCode, sizeof (javaMidiByteCode)) #undef JNI_CLASS_MEMBERS #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ - METHOD (getJuceAndroidMidiInputDevices, "getJuceAndroidMidiInputDevices", "()[Ljava/lang/String;") \ - METHOD (getJuceAndroidMidiOutputDevices, "getJuceAndroidMidiOutputDevices", "()[Ljava/lang/String;") \ - METHOD (openMidiInputPortWithJuceIndex, "openMidiInputPortWithJuceIndex", "(IJ)Lcom/roli/juce/JuceMidiSupport$JuceMidiPort;") \ - METHOD (openMidiOutputPortWithJuceIndex, "openMidiOutputPortWithJuceIndex", "(I)Lcom/roli/juce/JuceMidiSupport$JuceMidiPort;") \ - METHOD (getInputPortNameForJuceIndex, "getInputPortNameForJuceIndex", "(I)Ljava/lang/String;") \ - METHOD (getOutputPortNameForJuceIndex, "getOutputPortNameForJuceIndex", "(I)Ljava/lang/String;") + METHOD (getJuceAndroidMidiInputDeviceNameAndIDs, "getJuceAndroidMidiInputDeviceNameAndIDs", "()[Ljava/lang/String;") \ + METHOD (getJuceAndroidMidiOutputDeviceNameAndIDs, "getJuceAndroidMidiOutputDeviceNameAndIDs", "()[Ljava/lang/String;") \ + METHOD (openMidiInputPortWithID, "openMidiInputPortWithID", "(IJ)Lcom/roli/juce/JuceMidiSupport$JuceMidiPort;") \ + METHOD (openMidiOutputPortWithID, "openMidiOutputPortWithID", "(I)Lcom/roli/juce/JuceMidiSupport$JuceMidiPort;") DECLARE_JNI_CLASS_WITH_MIN_SDK (MidiDeviceManager, "com/roli/juce/JuceMidiSupport$MidiDeviceManager", 23) #undef JNI_CLASS_MEMBERS #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ - METHOD (start, "start", "()V" )\ - METHOD (stop, "stop", "()V") \ - METHOD (close, "close", "()V") \ - METHOD (sendMidi, "sendMidi", "([BII)V") + METHOD (start, "start", "()V") \ + METHOD (stop, "stop", "()V") \ + METHOD (close, "close", "()V") \ + METHOD (sendMidi, "sendMidi", "([BII)V") \ + METHOD (getName, "getName", "()Ljava/lang/String;") DECLARE_JNI_CLASS_WITH_MIN_SDK (JuceMidiPort, "com/roli/juce/JuceMidiSupport$JuceMidiPort", 23) #undef JNI_CLASS_MEMBERS @@ -368,15 +354,10 @@ DECLARE_JNI_CLASS_WITH_MIN_SDK (JuceMidiPort, "com/roli/juce/JuceMidiSupport$Juc class AndroidMidiInput { public: - AndroidMidiInput (MidiInput* midiInput, int portIdx, - juce::MidiInputCallback* midiInputCallback, jobject deviceManager) - : juceMidiInput (midiInput), - callback (midiInputCallback), - midiConcatenator (2048), - javaMidiDevice (LocalRef(getEnv()->CallObjectMethod (deviceManager, - MidiDeviceManager.openMidiInputPortWithJuceIndex, - (jint) portIdx, - (jlong) this))) + AndroidMidiInput (MidiInput* midiInput, int deviceID, juce::MidiInputCallback* midiInputCallback, jobject deviceManager) + : juceMidiInput (midiInput), callback (midiInputCallback), midiConcatenator (2048), + javaMidiDevice (LocalRef(getEnv()->CallObjectMethod (deviceManager, MidiDeviceManager.openMidiInputPortWithID, + (jint) deviceID, (jlong) this))) { } @@ -408,12 +389,20 @@ public: callback = nullptr; } + String getName() const noexcept + { + if (jobject d = javaMidiDevice.get()) + return juceString (LocalRef ((jstring) getEnv()->CallObjectMethod (d, JuceMidiPort.getName))); + + return {}; + } + void handleMidi (jbyteArray byteArray, jlong offset, jint len, jlong timestamp) { auto* env = getEnv(); jassert (byteArray != nullptr); - jbyte* data = env->GetByteArrayElements (byteArray, nullptr); + auto* data = env->GetByteArrayElements (byteArray, nullptr); HeapBlock buffer (static_cast (len)); std::memcpy (buffer.get(), data + offset, static_cast (len)); @@ -466,6 +455,14 @@ public: byteArray, offset, len); } + String getName() const noexcept + { + if (jobject d = javaMidiDevice.get()) + return juceString (LocalRef ((jstring) getEnv()->CallObjectMethod (d, JuceMidiPort.getName))); + + return {}; + } + private: GlobalRef javaMidiDevice; }; @@ -482,54 +479,42 @@ class AndroidMidiDeviceManager { public: AndroidMidiDeviceManager() - : deviceManager (LocalRef(getEnv()->CallStaticObjectMethod (JuceMidiSupport, JuceMidiSupport.getAndroidMidiDeviceManager, getAppContext().get()))) + : deviceManager (LocalRef(getEnv()->CallStaticObjectMethod (JuceMidiSupport, + JuceMidiSupport.getAndroidMidiDeviceManager, + getAppContext().get()))) { } - String getInputPortNameForJuceIndex (int idx) + Array getDevices (bool input) { if (jobject dm = deviceManager.get()) { - LocalRef string ((jstring) getEnv()->CallObjectMethod (dm, MidiDeviceManager.getInputPortNameForJuceIndex, idx)); - return juceString (string); + jobjectArray jDeviceNameAndIDs + = (jobjectArray) getEnv()->CallObjectMethod (dm, input ? MidiDeviceManager.getJuceAndroidMidiInputDeviceNameAndIDs + : MidiDeviceManager.getJuceAndroidMidiOutputDeviceNameAndIDs); + + // Create a local reference as converting this to a JUCE string will call into JNI + LocalRef localDeviceNameAndIDs (jDeviceNameAndIDs); + + auto deviceNameAndIDs = javaStringArrayToJuce (localDeviceNameAndIDs); + deviceNameAndIDs.appendNumbersToDuplicates (false, false, CharPointer_UTF8 ("-"), CharPointer_UTF8 ("")); + + Array devices; + + for (int i = 0; i < deviceNameAndIDs.size(); i += 2) + devices.add ({ deviceNameAndIDs[i], deviceNameAndIDs[i + 1] }); + + return devices; } return {}; } - String getOutputPortNameForJuceIndex (int idx) + AndroidMidiInput* openMidiInputPortWithID (int deviceID, MidiInput* juceMidiInput, juce::MidiInputCallback* callback) { - if (jobject dm = deviceManager.get()) + if (auto dm = deviceManager.get()) { - LocalRef string ((jstring) getEnv()->CallObjectMethod (dm, MidiDeviceManager.getOutputPortNameForJuceIndex, idx)); - return juceString (string); - } - - return {}; - } - - StringArray getDevices (bool input) - { - if (jobject dm = deviceManager.get()) - { - jobjectArray jDevices - = (jobjectArray) getEnv()->CallObjectMethod (dm, input ? MidiDeviceManager.getJuceAndroidMidiInputDevices - : MidiDeviceManager.getJuceAndroidMidiOutputDevices); - - // Create a local reference as converting this - // to a JUCE string will call into JNI - LocalRef devices (jDevices); - return javaStringArrayToJuce (devices); - } - - return {}; - } - - AndroidMidiInput* openMidiInputPortWithIndex (int idx, MidiInput* juceMidiInput, juce::MidiInputCallback* callback) - { - if (jobject dm = deviceManager.get()) - { - std::unique_ptr androidMidiInput (new AndroidMidiInput (juceMidiInput, idx, callback, dm)); + std::unique_ptr androidMidiInput (new AndroidMidiInput (juceMidiInput, deviceID, callback, dm)); if (androidMidiInput->isOpen()) return androidMidiInput.release(); @@ -538,10 +523,10 @@ public: return nullptr; } - AndroidMidiOutput* openMidiOutputPortWithIndex (int idx) + AndroidMidiOutput* openMidiOutputPortWithID (int deviceID) { - if (jobject dm = deviceManager.get()) - if (jobject javaMidiPort = getEnv()->CallObjectMethod (dm, MidiDeviceManager.openMidiOutputPortWithJuceIndex, (jint) idx)) + if (auto dm = deviceManager.get()) + if (auto javaMidiPort = getEnv()->CallObjectMethod (dm, MidiDeviceManager.openMidiOutputPortWithID, (jint) deviceID)) return new AndroidMidiOutput (LocalRef(javaMidiPort)); return nullptr; @@ -552,47 +537,146 @@ private: }; //============================================================================== -StringArray MidiOutput::getDevices() +Array MidiInput::getAvailableDevices() { - if (getAndroidSDKVersion() >= 23) + if (getAndroidSDKVersion() < 23) + return {}; + + AndroidMidiDeviceManager manager; + return manager.getDevices (true); +} + +MidiDeviceInfo MidiInput::getDefaultDevice() +{ + if (getAndroidSDKVersion() < 23) + return {}; + + return getAvailableDevices().getFirst(); +} + +std::unique_ptr MidiInput::openDevice (const String& deviceIdentifier, MidiInputCallback* callback) +{ + if (getAndroidSDKVersion() < 23 || deviceIdentifier.isEmpty()) + return {}; + + AndroidMidiDeviceManager manager; + + std::unique_ptr midiInput (new MidiInput ({}, deviceIdentifier)); + + if (auto* port = manager.openMidiInputPortWithID (deviceIdentifier.getIntValue(), midiInput.get(), callback)) { - AndroidMidiDeviceManager manager; - return manager.getDevices (false); + midiInput->internal = port; + midiInput->setName (port->getName()); + + return midiInput; } return {}; } -int MidiOutput::getDefaultDeviceIndex() +StringArray MidiInput::getDevices() { - return (getAndroidSDKVersion() >= 23 ? 0 : -1); + if (getAndroidSDKVersion() < 23) + return {}; + + StringArray deviceNames; + + for (auto& d : getAvailableDevices()) + deviceNames.add (d.name); + + return deviceNames; } -MidiOutput* MidiOutput::openDevice (int index) +int MidiInput::getDefaultDeviceIndex() { - if (index < 0 || getAndroidSDKVersion() < 23) - return nullptr; + return (getAndroidSDKVersion() < 23 ? -1 : 0); +} + +std::unique_ptr MidiInput::openDevice (int index, MidiInputCallback* callback) +{ + return openDevice (getAvailableDevices()[index].identifier, callback); +} + +MidiInput::MidiInput (const String& deviceName, const String& deviceIdentifier) + : deviceInfo (deviceName, deviceIdentifier) +{ +} + +MidiInput::~MidiInput() +{ + delete reinterpret_cast (internal); +} + +void MidiInput::start() +{ + if (auto* mi = reinterpret_cast (internal)) + mi->start(); +} + +void MidiInput::stop() +{ + if (auto* mi = reinterpret_cast (internal)) + mi->stop(); +} + +//============================================================================== +Array MidiOutput::getAvailableDevices() +{ + if (getAndroidSDKVersion() < 23) + return {}; + + AndroidMidiDeviceManager manager; + return manager.getDevices (false); +} + +MidiDeviceInfo MidiOutput::getDefaultDevice() +{ + if (getAndroidSDKVersion() < 23) + return {}; + + return getAvailableDevices().getFirst(); +} + +std::unique_ptr MidiOutput::openDevice (const String& deviceIdentifier) +{ + if (getAndroidSDKVersion() < 23 || deviceIdentifier.isEmpty()) + return {}; AndroidMidiDeviceManager manager; - String midiOutputName = manager.getOutputPortNameForJuceIndex (index); - - if (midiOutputName.isEmpty()) + if (auto* port = manager.openMidiOutputPortWithID (deviceIdentifier.getIntValue())) { - // you supplied an invalid device index! - jassertfalse; - return nullptr; + std::unique_ptr midiOutput (new MidiOutput ({}, deviceIdentifier)); + midiOutput->internal = port; + midiOutput->setName (port->getName()); + + return midiOutput; } - if (AndroidMidiOutput* midiOutput = manager.openMidiOutputPortWithIndex (index)) - { - MidiOutput* retval = new MidiOutput (midiOutputName); - retval->internal = midiOutput; + return {}; +} - return retval; - } +StringArray MidiOutput::getDevices() +{ + if (getAndroidSDKVersion() < 23) + return {}; - return nullptr; + StringArray deviceNames; + + for (auto& d : getAvailableDevices()) + deviceNames.add (d.name); + + return deviceNames; +} + +int MidiOutput::getDefaultDeviceIndex() +{ + return (getAndroidSDKVersion() < 23 ? -1 : 0); +} + +std::unique_ptr MidiOutput::openDevice (int index) +{ + return openDevice (getAvailableDevices()[index].identifier); } MidiOutput::~MidiOutput() @@ -604,15 +688,15 @@ MidiOutput::~MidiOutput() void MidiOutput::sendMessageNow (const MidiMessage& message) { - if (AndroidMidiOutput* androidMidi = reinterpret_cast(internal)) + if (auto* androidMidi = reinterpret_cast(internal)) { - JNIEnv* env = getEnv(); - const int messageSize = message.getRawDataSize(); + auto* env = getEnv(); + auto messageSize = message.getRawDataSize(); - LocalRef messageContent = LocalRef (env->NewByteArray (messageSize)); - jbyteArray content = messageContent.get(); + LocalRef messageContent (env->NewByteArray (messageSize)); + auto content = messageContent.get(); - jbyte* rawBytes = env->GetByteArrayElements (content, nullptr); + auto* rawBytes = env->GetByteArrayElements (content, nullptr); std::memcpy (rawBytes, message.getRawData(), static_cast (messageSize)); env->ReleaseByteArrayElements (content, rawBytes, 0); @@ -620,66 +704,4 @@ void MidiOutput::sendMessageNow (const MidiMessage& message) } } -//============================================================================== -MidiInput::MidiInput (const String& nm) : name (nm) -{ -} - -StringArray MidiInput::getDevices() -{ - if (getAndroidSDKVersion() >= 23) - { - AndroidMidiDeviceManager manager; - return manager.getDevices (true); - } - - return {}; -} - -int MidiInput::getDefaultDeviceIndex() -{ - return (getAndroidSDKVersion() >= 23 ? 0 : -1); -} - -MidiInput* MidiInput::openDevice (int index, juce::MidiInputCallback* callback) -{ - if (getAndroidSDKVersion() < 23 || index < 0) - return nullptr; - - AndroidMidiDeviceManager manager; - - String midiInputName (manager.getInputPortNameForJuceIndex (index)); - - if (midiInputName.isEmpty()) - { - // you supplied an invalid device index! - jassertfalse; - return nullptr; - } - - std::unique_ptr midiInput (new MidiInput (midiInputName)); - - midiInput->internal = manager.openMidiInputPortWithIndex (index, midiInput.get(), callback); - - return midiInput->internal != nullptr ? midiInput.release() - : nullptr; -} - -void MidiInput::start() -{ - if (AndroidMidiInput* mi = reinterpret_cast (internal)) - mi->start(); -} - -void MidiInput::stop() -{ - if (AndroidMidiInput* mi = reinterpret_cast (internal)) - mi->stop(); -} - -MidiInput::~MidiInput() -{ - delete reinterpret_cast (internal); -} - } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Oboe.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Oboe.cpp index 5e18780..4d20250 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Oboe.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Oboe.cpp @@ -424,7 +424,7 @@ private: else { for (int i = 0; i < numOutputChannels; ++i) - zeromem (outputChannelData[i], sizeof (float) * static_cast (numFrames)); + zeromem (outputChannelData[i], (size_t) (numFrames) * sizeof (float)); } } @@ -1434,7 +1434,7 @@ public: } private: - //============================================================================= + //============================================================================== void* (*threadEntryProc) (void*) = nullptr; void* threadUserPtr = nullptr; diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp index dd3eb51..4a19f77 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp @@ -288,6 +288,40 @@ struct BufferHelpers } }; +//============================================================================== +using CreateEngineFunc = SLresult (*) (SLObjectItf*, SLuint32, const SLEngineOption*, + SLuint32, const SLInterfaceID*, const SLboolean*); + +struct OpenSLEngineHolder +{ + OpenSLEngineHolder() + { + if (auto createEngine = (CreateEngineFunc) slLibrary.getFunction ("slCreateEngine")) + { + SLObjectItf obj = nullptr; + auto err = createEngine (&obj, 0, nullptr, 0, nullptr, nullptr); + + if (err != SL_RESULT_SUCCESS || obj == nullptr || *obj == nullptr + || (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS) + { + destroyObject (obj); + } + + engine = SlRef::cast (SlObjectRef (obj)); + } + } + + DynamicLibrary slLibrary { "libOpenSLES.so" }; + SlRef engine; +}; + +OpenSLEngineHolder& getEngineHolder() +{ + static OpenSLEngineHolder holder; + return holder; +} + +//============================================================================== class SLRealtimeThread; //============================================================================== @@ -340,7 +374,7 @@ public: auto status = (*config)->AcquireJavaProxy (config, /*SL_ANDROID_JAVA_PROXY_ROUTING*/1, &audioRoutingJni); - if (status == SL_RESULT_SUCCESS && audioRoutingJni != 0) + if (status == SL_RESULT_SUCCESS && audioRoutingJni != nullptr) javaProxy = GlobalRef (LocalRef(getEnv()->NewLocalRef (audioRoutingJni))); } } @@ -430,9 +464,11 @@ public: SLObjectItf obj = nullptr; - if (auto e = *Base::owner.engine) + auto& holder = getEngineHolder(); + + if (auto e = *holder.engine) { - auto status = e->CreateAudioPlayer (Base::owner.engine, &obj, &source, &sink, 2, + auto status = e->CreateAudioPlayer (holder.engine, &obj, &source, &sink, 2, queueInterfaces, interfaceRequired); if (status != SL_RESULT_SUCCESS || obj == nullptr || (*obj)->Realize(obj, 0) != SL_RESULT_SUCCESS) @@ -473,9 +509,11 @@ public: SLObjectItf obj = nullptr; - if (auto e = *Base::owner.engine) + auto& holder = getEngineHolder(); + + if (auto e = *holder.engine) { - auto status = e->CreateAudioRecorder (Base::owner.engine, &obj, &source, &sink, 2, queueInterfaces, interfaceRequired); + auto status = e->CreateAudioRecorder (holder.engine, &obj, &source, &sink, 2, queueInterfaces, interfaceRequired); if (status != SL_RESULT_SUCCESS || obj == nullptr || (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS) { @@ -514,8 +552,7 @@ public: class OpenSLSession { public: - OpenSLSession (DynamicLibrary& slLibraryToUse, - int numInputChannels, int numOutputChannels, + OpenSLSession (int numInputChannels, int numOutputChannels, double samleRateToUse, int bufferSizeToUse, int numBuffersToUse) : inputChannels (numInputChannels), outputChannels (numOutputChannels), @@ -523,25 +560,12 @@ public: { jassert (numInputChannels > 0 || numOutputChannels > 0); - if (auto createEngine = (CreateEngineFunc) slLibraryToUse.getFunction ("slCreateEngine")) - { - SLObjectItf obj = nullptr; - auto err = createEngine (&obj, 0, nullptr, 0, nullptr, nullptr); - - if (err != SL_RESULT_SUCCESS || obj == nullptr || *obj == nullptr - || (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS) - { - destroyObject (obj); - return; - } - - engine = SlRef::cast (SlObjectRef (obj)); - } - if (outputChannels > 0) { + auto& holder = getEngineHolder(); SLObjectItf obj = nullptr; - auto err = (*engine)->CreateOutputMix (engine, &obj, 0, nullptr, nullptr); + + auto err = (*holder.engine)->CreateOutputMix (holder.engine, &obj, 0, nullptr, nullptr); if (err != SL_RESULT_SUCCESS || obj == nullptr || *obj == nullptr || (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS) @@ -556,7 +580,7 @@ public: virtual ~OpenSLSession() {} - virtual bool openedOK() const { return (engine != nullptr && (outputChannels == 0 || (outputMix != nullptr))); } + virtual bool openedOK() const { return (outputChannels == 0 || outputMix != nullptr); } virtual void start() { stop(); jassert (callback.get() != nullptr); running = true; } virtual void stop() { running = false; } @@ -604,22 +628,16 @@ public: } } - static OpenSLSession* create (DynamicLibrary& slLibrary, - int numInputChannels, int numOutputChannels, + static OpenSLSession* create (int numInputChannels, int numOutputChannels, double samleRateToUse, int bufferSizeToUse, int numBuffersToUse); - //============================================================================== - using CreateEngineFunc = SLresult (*) (SLObjectItf*, SLuint32, const SLEngineOption*, - SLuint32, const SLInterfaceID*, const SLboolean*); - //============================================================================== int inputChannels, outputChannels; double sampleRate; int bufferSize, numBuffers; bool running = false, audioProcessingEnabled = true; - SlRef engine; SlRef outputMix; Atomic callback { nullptr }; @@ -629,11 +647,10 @@ public: class OpenSLSessionT : public OpenSLSession { public: - OpenSLSessionT (DynamicLibrary& slLibraryToUse, - int numInputChannels, int numOutputChannels, + OpenSLSessionT (int numInputChannels, int numOutputChannels, double samleRateToUse, int bufferSizeToUse, int numBuffersToUse) - : OpenSLSession (slLibraryToUse, numInputChannels, numOutputChannels, + : OpenSLSession (numInputChannels, numOutputChannels, samleRateToUse, bufferSizeToUse, numBuffersToUse) { jassert (numInputChannels > 0 || numOutputChannels > 0); @@ -662,7 +679,7 @@ public: } const bool supportsUnderrunCount = (getAndroidSDKVersion() >= 24); - getUnderrunCount = supportsUnderrunCount ? getEnv()->GetMethodID (AudioTrack, "getUnderrunCount", "()I") : 0; + getUnderrunCount = supportsUnderrunCount ? getEnv()->GetMethodID (AudioTrack, "getUnderrunCount", "()I") : nullptr; } } } @@ -727,7 +744,7 @@ public: int getXRunCount() const noexcept override { - if (player != nullptr && player->javaProxy != nullptr && getUnderrunCount != 0) + if (player != nullptr && player->javaProxy != nullptr && getUnderrunCount != nullptr) return getEnv()->CallIntMethod (player->javaProxy, getUnderrunCount); return -1; @@ -740,7 +757,7 @@ public: // only the player or the recorder should enter this section at any time if (guard.compareAndSetBool (1, 0)) { - // are there enough buffers avaialable to process some audio + // are there enough buffers available to process some audio if ((inputChannels == 0 || recorder->isBufferAvailable()) && (outputChannels == 0 || player->isBufferAvailable())) { T* recorderBuffer = (inputChannels > 0 ? recorder->getNextBuffer() : nullptr); @@ -783,7 +800,7 @@ public: std::unique_ptr> player; std::unique_ptr> recorder; Atomic guard; - jmethodID getUnderrunCount = 0; + jmethodID getUnderrunCount = nullptr; }; //============================================================================== @@ -803,14 +820,11 @@ public: inputLatency = (int) ((longestLatency * inputLatency) / totalLatency) & ~15; outputLatency = (int) ((longestLatency * outputLatency) / totalLatency) & ~15; - bool success = slLibrary.open ("libOpenSLES.so"); - // You can only create this class if you are sure that your hardware supports OpenSL - jassert (success); - ignoreUnused (success); + jassert (getEngineHolder().slLibrary.getNativeHandle() != nullptr); } - ~OpenSLAudioIODevice() + ~OpenSLAudioIODevice() override { close(); } @@ -840,7 +854,7 @@ public: 22050.0, 24000.0, 32000.0, 44100.0, 48000.0 }; Array retval (rates, numElementsInArray (rates)); - // make sure the native sample rate is pafrt of the list + // make sure the native sample rate is part of the list double native = getNativeSampleRate(); if (native != 0.0 && ! retval.contains (native)) @@ -899,7 +913,7 @@ public: lastError = "Error opening OpenSL input device: the app was not granted android.permission.RECORD_AUDIO"; } - session.reset (OpenSLSession::create (slLibrary, numInputChannels, numOutputChannels, + session.reset (OpenSLSession::create (numInputChannels, numOutputChannels, sampleRate, actualBufferSize, audioBuffersToEnqueue)); if (session != nullptr) { @@ -913,7 +927,7 @@ public: activeInputChans = BigInteger(0); numInputChannels = 0; - session.reset (OpenSLSession::create (slLibrary, numInputChannels, numOutputChannels, + session.reset (OpenSLSession::create (numInputChannels, numOutputChannels, sampleRate, actualBufferSize, audioBuffersToEnqueue)); } } @@ -1025,7 +1039,6 @@ private: friend class SLRealtimeThread; //============================================================================== - DynamicLibrary slLibrary; int actualBufferSize = 0, sampleRate = 0, audioBuffersToEnqueue = 0; int inputLatency, outputLatency; bool deviceOpen = false, audioProcessingEnabled = true; @@ -1123,8 +1136,7 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenSLAudioIODevice) }; -OpenSLAudioIODevice::OpenSLSession* OpenSLAudioIODevice::OpenSLSession::create (DynamicLibrary& slLibrary, - int numInputChannels, int numOutputChannels, +OpenSLAudioIODevice::OpenSLSession* OpenSLAudioIODevice::OpenSLSession::create (int numInputChannels, int numOutputChannels, double samleRateToUse, int bufferSizeToUse, int numBuffersToUse) { @@ -1134,7 +1146,7 @@ OpenSLAudioIODevice::OpenSLSession* OpenSLAudioIODevice::OpenSLSession::create ( // SDK versions 21 and higher should natively support floating point... if (sdkVersion >= 21) { - retval.reset (new OpenSLSessionT (slLibrary, numInputChannels, numOutputChannels, samleRateToUse, + retval.reset (new OpenSLSessionT (numInputChannels, numOutputChannels, samleRateToUse, bufferSizeToUse, numBuffersToUse)); // ...however, some devices lie so re-try without floating point @@ -1144,7 +1156,7 @@ OpenSLAudioIODevice::OpenSLSession* OpenSLAudioIODevice::OpenSLSession::create ( if (retval == nullptr) { - retval.reset (new OpenSLSessionT (slLibrary, numInputChannels, numOutputChannels, samleRateToUse, + retval.reset (new OpenSLSessionT (numInputChannels, numOutputChannels, samleRateToUse, bufferSizeToUse, numBuffersToUse)); if (retval != nullptr && (! retval->openedOK())) @@ -1186,9 +1198,6 @@ public: return library.open ("libOpenSLES.so"); } -private: - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenSLAudioDeviceType) }; @@ -1211,7 +1220,7 @@ public: SLRealtimeThread() { - if (auto createEngine = (OpenSLAudioIODevice::OpenSLSession::CreateEngineFunc) slLibrary.getFunction ("slCreateEngine")) + if (auto createEngine = (CreateEngineFunc) slLibrary.getFunction ("slCreateEngine")) { SLObjectItf obj = nullptr; auto err = createEngine (&obj, 0, nullptr, 0, nullptr, nullptr); @@ -1342,13 +1351,13 @@ public: } private: - //============================================================================= + //============================================================================== static void staticFinished (SLAndroidSimpleBufferQueueItf, void* context) { static_cast (context)->finished(); } - //============================================================================= + //============================================================================== DynamicLibrary slLibrary { "libOpenSLES.so" }; SlRef engine; diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp index ab9bc12..23da27e 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp @@ -216,7 +216,7 @@ class iOSAudioIODeviceType : public AudioIODeviceType, { public: iOSAudioIODeviceType(); - ~iOSAudioIODeviceType(); + ~iOSAudioIODeviceType() override; void scanForDevices() override; StringArray getDeviceNames (bool) const override; @@ -262,7 +262,7 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, sessionHolder->activeDevices.add (this); } - ~Pimpl() + ~Pimpl() override { sessionHolder->activeDevices.removeFirstMatchingValue (this); @@ -698,7 +698,7 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, &dataSize); if (err == noErr) { - #if (! defined __IPHONE_OS_VERSION_MIN_REQUIRED) || (! defined __IPHONE_10_0) || (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0) + #if (! defined __IPHONE_10_0) || (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0) [[UIApplication sharedApplication] openURL: (NSURL*)hostUrl]; #else [[UIApplication sharedApplication] openURL: (NSURL*)hostUrl options: @{} completionHandler: nil]; diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp index d1ecf48..f126c1d 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp @@ -261,7 +261,7 @@ public: unsigned int periods = 4; snd_pcm_uframes_t samplesPerPeriod = (snd_pcm_uframes_t) bufferSize; - if (JUCE_ALSA_FAILED (snd_pcm_hw_params_set_rate_near (handle, hwParams, &sampleRate, 0)) + if (JUCE_ALSA_FAILED (snd_pcm_hw_params_set_rate_near (handle, hwParams, &sampleRate, nullptr)) || JUCE_ALSA_FAILED (snd_pcm_hw_params_set_channels (handle, hwParams, (unsigned int ) numChannels)) || JUCE_ALSA_FAILED (snd_pcm_hw_params_set_periods_near (handle, hwParams, &periods, &dir)) || JUCE_ALSA_FAILED (snd_pcm_hw_params_set_period_size_near (handle, hwParams, &samplesPerPeriod, &dir)) @@ -494,7 +494,7 @@ public: initialiseRatesAndChannels(); } - ~ALSAThread() + ~ALSAThread() override { close(); } @@ -722,7 +722,7 @@ public: else { for (int i = 0; i < outputChannelDataForCallback.size(); ++i) - zeromem (outputChannelDataForCallback[i], sizeof (float) * (size_t) bufferSize); + zeromem (outputChannelDataForCallback[i], (size_t) bufferSize * sizeof (float)); } } @@ -853,7 +853,7 @@ public: { } - ~ALSAAudioIODevice() + ~ALSAAudioIODevice() override { close(); } @@ -1249,7 +1249,7 @@ private: snd_device_name_free_hint (hints); } - // sometimes the "default" device is not listed, but it is nice to see it explicitely in the list + // sometimes the "default" device is not listed, but it is nice to see it explicitly in the list if (! outputIds.contains ("default")) testDevice ("default", "Default ALSA Output", "Default ALSA Input"); diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Bela.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Bela.cpp index a4d000e..133e39b 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Bela.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Bela.cpp @@ -34,6 +34,8 @@ public: { jassert (midiCallback != nullptr); midiInputs.add (this); + + buffer.resize (32); } ~BelaMidiInput() @@ -54,6 +56,8 @@ public: void poll() { + size_t receivedBytes = 0; + for (;;) { auto data = midi.getInput(); @@ -61,14 +65,23 @@ public: if (data < 0) break; - auto byte = (uint8) data; - concatenator.pushMidiData (&byte, 1, 0.0, midiInput, *midiCallback); + buffer[receivedBytes] = (uint8) data; + receivedBytes++; + + if (receivedBytes == buffer.size()) + { + pushMidiData (static_cast (receivedBytes)); + receivedBytes = 0; + } } + + if (receivedBytes > 0) + pushMidiData (receivedBytes); } - static StringArray getDevices (bool input) + static Array getDevices (bool input) { - StringArray devices; + Array devices; for (auto& card : findAllALSACardIDs()) findMidiDevices (devices, input, card); @@ -76,7 +89,19 @@ public: return devices; } + void pushMidiMessage (juce::MidiMessage& message) + { + concatenator.pushMidiData (message.getRawData(), message.getRawDataSize(), Time::getMillisecondCounter() * 0.001, midiInput, *midiCallback); + } + private: + void pushMidiData (int length) + { + concatenator.pushMidiData (buffer.data(), length, Time::getMillisecondCounter() * 0.001, midiInput, *midiCallback); + } + + std::vector buffer; + static Array findAllALSACardIDs() { Array cards; @@ -96,7 +121,7 @@ private: } // Adds all midi devices to the devices array of the given input/output type on the given card - static void findMidiDevices (StringArray& devices, bool input, int cardNum) + static void findMidiDevices (Array& devices, bool input, int cardNum) { snd_ctl_t* ctl = nullptr; auto status = snd_ctl_open (&ctl, ("hw:" + String (cardNum)).toRawUTF8(), 0); @@ -124,24 +149,25 @@ private: auto subCount = snd_rawmidi_info_get_subdevices_count (info); - for (int sub = 0; sub < subCount; ++sub) + for (size_t sub = 0; sub < subCount; ++sub) { snd_rawmidi_info_set_subdevice (info, sub); status = snd_ctl_rawmidi_info (ctl, info); if (status == 0) - devices.add ("hw:" + String (cardNum) + "," - + String (device) + "," - + String (sub)); + { + String deviceName ("hw:" + String (cardNum) + "," + String (device) + "," + String (sub)); + devices.add (MidiDeviceInfo (deviceName, deviceName)); + } } } snd_ctl_close (ctl); } - String midiPort; MidiInput* const midiInput; + String midiPort; MidiInputCallback* const midiCallback; Midi midi; @@ -169,8 +195,26 @@ public: } //============================================================================== - StringArray getOutputChannelNames() override { return { "Out #1", "Out #2" }; } - StringArray getInputChannelNames() override { return { "In #1", "In #2" }; } + StringArray getOutputChannelNames() override + { + StringArray result; + + for (int i = 1; i <= actualNumberOfOutputs; i++) + result.add ("Out #" + std::to_string (i)); + + return result; + } + + StringArray getInputChannelNames() override + { + StringArray result; + + for (int i = 1; i <= actualNumberOfInputs; i++) + result.add ("In #" + std::to_string (i)); + + return result; + } + Array getAvailableSampleRates() override { return { 44100.0 }; } Array getAvailableBufferSizes() override { /* TODO: */ return { getDefaultBufferSize() }; } int getDefaultBufferSize() override { return defaultSettings.periodSize; } @@ -192,15 +236,27 @@ public: auto numIns = getNumContiguousSetBits (inputChannels); auto numOuts = getNumContiguousSetBits (outputChannels); - settings.useAnalog = 0; - settings.useDigital = 0; - settings.numAudioInChannels = numIns; - settings.numAudioOutChannels = numOuts; + // Input and Output channels are numbered as follows + // + // 0 .. 1 - audio + // 2 .. 9 - analog + + if (numIns > 2 || numOuts > 2) + { + settings.useAnalog = true; + settings.numAnalogInChannels = std::max (numIns - 2, 8); + settings.numAnalogOutChannels = std::max (numOuts - 2, 8); + settings.uniformSampleRate = true; + } + + settings.numAudioInChannels = std::max (numIns, 2); + settings.numAudioOutChannels = std::max (numOuts, 2); + settings.detectUnderruns = 1; settings.setup = setupCallback; settings.render = renderCallback; settings.cleanup = cleanupCallback; - settings.interleave = 1; + settings.interleave = 0; if (bufferSizeSamples > 0) settings.periodSize = bufferSizeSamples; @@ -219,10 +275,7 @@ public: actualNumberOfInputs = jmin (numIns, actualNumberOfInputs); actualNumberOfOutputs = jmin (numOuts, actualNumberOfOutputs); - audioInBuffer.setSize (actualNumberOfInputs, actualBufferSize); channelInBuffer.calloc (actualNumberOfInputs); - - audioOutBuffer.setSize (actualNumberOfOutputs, actualBufferSize); channelOutBuffer.calloc (actualNumberOfOutputs); return {}; @@ -244,10 +297,7 @@ public: actualNumberOfInputs = 0; actualNumberOfOutputs = 0; - audioInBuffer.setSize (0, 0); channelInBuffer.free(); - - audioOutBuffer.setSize (0, 0); channelOutBuffer.free(); } } @@ -277,9 +327,6 @@ public: } else { - audioInBuffer.clear(); - audioOutBuffer.clear(); - callback = newCallback; isRunning = (Bela_startAudio() == 0); @@ -321,23 +368,24 @@ public: //============================================================================== int getCurrentBufferSizeSamples() override { return actualBufferSize; } double getCurrentSampleRate() override { return 44100.0; } - int getCurrentBitDepth() override { return 24; } + int getCurrentBitDepth() override { return 16; } BigInteger getActiveOutputChannels() const override { BigInteger b; b.setRange (0, actualNumberOfOutputs, true); return b; } BigInteger getActiveInputChannels() const override { BigInteger b; b.setRange (0, actualNumberOfInputs, true); return b; } int getOutputLatencyInSamples() override { /* TODO */ return 0; } int getInputLatencyInSamples() override { /* TODO */ return 0; } - int getXRunCount() const noexcept { return underruns; } + int getXRunCount() const noexcept override { return underruns; } //============================================================================== static const char* const belaTypeName; private: + //============================================================================== bool setup (BelaContext& context) { actualBufferSize = context.audioFrames; - actualNumberOfInputs = context.audioInChannels; - actualNumberOfOutputs = context.audioOutChannels; + actualNumberOfInputs = context.audioInChannels + context.analogInChannels; + actualNumberOfOutputs = context.audioOutChannels + context.analogOutChannels; isBelaOpen = true; firstCallback = true; @@ -363,59 +411,29 @@ private: if (callback != nullptr) { jassert (context.audioFrames <= actualBufferSize); - auto numSamples = jmin (context.audioFrames, actualBufferSize); - auto interleaved = ((context.flags & BELA_FLAG_INTERLEAVED) != 0); - auto numIns = jmin (actualNumberOfInputs, (int) context.audioInChannels); - auto numOuts = jmin (actualNumberOfOutputs, (int) context.audioOutChannels); + jassert ((context.flags & BELA_FLAG_INTERLEAVED) == 0); - int ch; - - if (interleaved && context.audioInChannels > 1) + // Setup channelInBuffers + for (int ch = 0; ch < actualNumberOfInputs; ++ch) { - for (ch = 0; ch < numIns; ++ch) - { - using DstSampleType = AudioData::Pointer; - using SrcSampleType = AudioData::Pointer; - - channelInBuffer[ch] = audioInBuffer.getWritePointer (ch); - DstSampleType dstData (audioInBuffer.getWritePointer (ch)); - SrcSampleType srcData (context.audioIn + ch, context.audioInChannels); - dstData.convertSamples (srcData, numSamples); - } - } - else - { - for (ch = 0; ch < numIns; ++ch) - channelInBuffer[ch] = context.audioIn + (ch * numSamples); + if (ch < analogChannelStart) + channelInBuffer[ch] = &context.audioIn[ch * context.audioFrames]; + else + channelInBuffer[ch] = &context.analogIn[(ch - analogChannelStart) * context.analogFrames]; } - for (; ch < actualNumberOfInputs; ++ch) + // Setup channelOutBuffers + for (int ch = 0; ch < actualNumberOfOutputs; ++ch) { - channelInBuffer[ch] = audioInBuffer.getWritePointer(ch); - zeromem (audioInBuffer.getWritePointer (ch), sizeof (float) * numSamples); + if (ch < analogChannelStart) + channelOutBuffer[ch] = &context.audioOut[ch * context.audioFrames]; + else + channelOutBuffer[ch] = &context.analogOut[(ch - analogChannelStart) * context.audioFrames]; } - for (int i = 0; i < actualNumberOfOutputs; ++i) - channelOutBuffer[i] = ((interleaved && context.audioOutChannels > 1) || i >= context.audioOutChannels ? audioOutBuffer.getWritePointer (i) - : context.audioOut + (i * numSamples)); - callback->audioDeviceIOCallback (channelInBuffer.getData(), actualNumberOfInputs, channelOutBuffer.getData(), actualNumberOfOutputs, - numSamples); - - if (interleaved && context.audioOutChannels > 1) - { - for (int i = 0; i < numOuts; ++i) - { - using DstSampleType = AudioData::Pointer; - using SrcSampleType = AudioData::Pointer; - - SrcSampleType srcData (channelOutBuffer[i]); - DstSampleType dstData (context.audioOut + i, context.audioOutChannels); - - dstData.convertSamples (srcData, numSamples); - } - } + context.audioFrames); } } @@ -427,6 +445,7 @@ private: callback->audioDeviceStopped(); } + const int analogChannelStart = 2; //============================================================================== uint64_t expectedElapsedAudioSamples = 0; @@ -469,10 +488,11 @@ private: uint32_t actualBufferSize = 0; int actualNumberOfInputs = 0, actualNumberOfOutputs = 0; - AudioBuffer audioInBuffer, audioOutBuffer; HeapBlock channelInBuffer; HeapBlock channelOutBuffer; + bool includeAnalogSupport; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BelaAudioIODevice) }; @@ -483,7 +503,6 @@ struct BelaAudioIODeviceType : public AudioIODeviceType { BelaAudioIODeviceType() : AudioIODeviceType ("Bela") {} - // TODO: support analog outputs StringArray getDeviceNames (bool) const override { return StringArray (BelaAudioIODevice::belaTypeName); } void scanForDevices() override {} int getDefaultDeviceIndex (bool) const override { return 0; } @@ -492,6 +511,7 @@ struct BelaAudioIODeviceType : public AudioIODeviceType AudioIODevice* createDevice (const String& outputName, const String& inputName) override { + // TODO: switching whether to support analog/digital with possible multiple Bela device types? if (outputName == BelaAudioIODevice::belaTypeName || inputName == BelaAudioIODevice::belaTypeName) return new BelaAudioIODevice(); @@ -507,58 +527,74 @@ AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() return new BelaAudioIODeviceType(); } - //============================================================================== -// TODO: Add Bela MidiOutput support - -StringArray MidiOutput::getDevices() { return {}; } -int MidiOutput::getDefaultDeviceIndex() { return 0; } -MidiOutput* MidiOutput::openDevice (int) { return {}; } -MidiOutput* MidiOutput::createNewDevice (const String&) { return {}; } -MidiOutput::~MidiOutput() {} -void MidiOutput::sendMessageNow (const MidiMessage&) {} - - -//============================================================================== -MidiInput::MidiInput (const String& nm) : name (nm) {} - -MidiInput::~MidiInput() +MidiInput::MidiInput (const String& deviceName, const String& deviceID) + : deviceInfo (deviceName, deviceID) { - delete static_cast (internal); } -void MidiInput::start() { static_cast (internal)->start(); } -void MidiInput::stop() { static_cast (internal)->stop(); } +MidiInput::~MidiInput() { delete static_cast (internal); } +void MidiInput::start() { static_cast (internal)->start(); } +void MidiInput::stop() { static_cast (internal)->stop(); } + +Array MidiInput::getAvailableDevices() +{ + return BelaMidiInput::getDevices (true); +} + +MidiDeviceInfo MidiInput::getDefaultDevice() +{ + return getAvailableDevices().getFirst(); +} + +std::unique_ptr MidiInput::openDevice (const String& deviceIdentifier, MidiInputCallback* callback) +{ + if (deviceIdentifier.isEmpty()) + return {}; + + std::unique_ptr midiInput (new MidiInput (deviceIdentifier, deviceIdentifier)); + midiInput->internal = new BelaMidiInput (deviceIdentifier, midiInput.get(), callback); + + return midiInput; +} + +std::unique_ptr MidiInput::createNewDevice (const String&, MidiInputCallback*) +{ + // N/A on Bela + jassertfalse; + return {}; +} + +StringArray MidiInput::getDevices() +{ + StringArray deviceNames; + + for (auto& d : getAvailableDevices()) + deviceNames.add (d.name); + + return deviceNames; +} int MidiInput::getDefaultDeviceIndex() { return 0; } -StringArray MidiInput::getDevices() +std::unique_ptr MidiInput::openDevice (int index, MidiInputCallback* callback) { - return BelaMidiInput::getDevices (true); + return openDevice (getAvailableDevices()[index].identifier, callback); } -MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) -{ - auto devices = getDevices(); - - if (index >= 0 && index < devices.size()) - { - auto deviceName = devices[index]; - auto result = new MidiInput (deviceName); - result->internal = new BelaMidiInput (deviceName, result, callback); - return result; - } - - return {}; -} - -MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback) -{ - jassertfalse; // N/A on Bela - return {}; -} +//============================================================================== +// TODO: Add Bela MidiOutput support +MidiOutput::~MidiOutput() {} +void MidiOutput::sendMessageNow (const MidiMessage&) {} +Array MidiOutput::getAvailableDevices() { return {}; } +MidiDeviceInfo MidiOutput::getDefaultDevice() { return {}; } +std::unique_ptr MidiOutput::openDevice (const String&) { return {}; } +std::unique_ptr MidiOutput::createNewDevice (const String&) { return {}; } +StringArray MidiOutput::getDevices() { return {}; } +int MidiOutput::getDefaultDeviceIndex() { return 0;} +std::unique_ptr MidiOutput::openDevice (int) { return {}; } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp index 35302a3..b916d7a 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp @@ -105,7 +105,11 @@ namespace //============================================================================== #ifndef JUCE_JACK_CLIENT_NAME - #define JUCE_JACK_CLIENT_NAME "JUCEJack" + #ifdef JucePlugin_Name + #define JUCE_JACK_CLIENT_NAME JucePlugin_Name + #else + #define JUCE_JACK_CLIENT_NAME "JUCEJack" + #endif #endif struct JackPortIterator diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Midi.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Midi.cpp index 8fa2643..8c70ed8 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Midi.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Midi.cpp @@ -67,12 +67,12 @@ public: static String getAlsaMidiName() { #ifdef JUCE_ALSA_MIDI_NAME - return JUCE_ALSA_MIDI_NAME; + return JUCE_ALSA_MIDI_NAME; #else - if (auto* app = JUCEApplicationBase::getInstance()) - return app->getApplicationName(); + if (auto* app = JUCEApplicationBase::getInstance()) + return app->getApplicationName(); - return "JUCE"; + return "JUCE"; #endif } @@ -198,7 +198,8 @@ public: isInput ? (SND_SEQ_PORT_CAP_WRITE | (enableSubscription ? SND_SEQ_PORT_CAP_SUBS_WRITE : 0)) : (SND_SEQ_PORT_CAP_READ | (enableSubscription ? SND_SEQ_PORT_CAP_SUBS_READ : 0)); - portId = snd_seq_create_simple_port (seqHandle, name.toUTF8(), caps, + portName = name; + portId = snd_seq_create_simple_port (seqHandle, portName.toUTF8(), caps, SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION); } @@ -215,13 +216,15 @@ public: } AlsaClient& client; + MidiInputCallback* callback = nullptr; snd_midi_event_t* midiParser = nullptr; MidiInput* midiInput = nullptr; - int maxEventSize = 4096; - int portId = -1; - bool callbackEnabled = false; - bool isInput = false; + + String portName; + + int maxEventSize = 4096, portId = -1; + bool callbackEnabled = false, isInput = false; }; static Ptr getInstance() @@ -276,7 +279,7 @@ public: void deletePort (Port* port) { - ports.remove (port->portId); + ports.set (port->portId, nullptr); decReferenceCount(); } @@ -359,11 +362,16 @@ private: AlsaClient* AlsaClient::instance = nullptr; //============================================================================== +static String getFormattedPortIdentifier (int clientId, int portId) +{ + return String (clientId) + "-" + String (portId); +} + static AlsaClient::Port* iterateMidiClient (const AlsaClient::Ptr& client, snd_seq_client_info_t* clientInfo, bool forInput, - StringArray& deviceNamesFound, - int deviceIndexToOpen) + Array& devices, + const String& deviceIdentifierToOpen) { AlsaClient::Port* port = nullptr; @@ -371,7 +379,7 @@ static AlsaClient::Port* iterateMidiClient (const AlsaClient::Ptr& client, snd_seq_port_info_t* portInfo = nullptr; snd_seq_port_info_alloca (&portInfo); - jassert (portInfo); + jassert (portInfo != nullptr); auto numPorts = snd_seq_client_info_get_num_ports (clientInfo); auto sourceClient = snd_seq_client_info_get_client (clientInfo); @@ -384,19 +392,19 @@ static AlsaClient::Port* iterateMidiClient (const AlsaClient::Ptr& client, && (snd_seq_port_info_get_capability (portInfo) & (forInput ? SND_SEQ_PORT_CAP_SUBS_READ : SND_SEQ_PORT_CAP_SUBS_WRITE)) != 0) { - String portName = snd_seq_port_info_get_name(portInfo); + String portName (snd_seq_port_info_get_name (portInfo)); + auto portID = snd_seq_port_info_get_port (portInfo); - deviceNamesFound.add (portName); + MidiDeviceInfo device (portName, getFormattedPortIdentifier (sourceClient, portID)); + devices.add (device); - if (deviceNamesFound.size() == deviceIndexToOpen + 1) + if (deviceIdentifierToOpen.isNotEmpty() && deviceIdentifierToOpen == device.identifier) { - auto sourcePort = snd_seq_port_info_get_port (portInfo); - - if (sourcePort != -1) + if (portID != -1) { port = client->createPort (portName, forInput, false); jassert (port->isValid()); - port->connectWith (sourceClient, sourcePort); + port->connectWith (sourceClient, portID); break; } } @@ -407,8 +415,8 @@ static AlsaClient::Port* iterateMidiClient (const AlsaClient::Ptr& client, } static AlsaClient::Port* iterateMidiDevices (bool forInput, - StringArray& deviceNamesFound, - int deviceIndexToOpen) + Array& devices, + const String& deviceIdentifierToOpen) { AlsaClient::Port* port = nullptr; auto client = AlsaClient::getInstance(); @@ -432,85 +440,96 @@ static AlsaClient::Port* iterateMidiDevices (bool forInput, { if (snd_seq_query_next_client (seqHandle, clientInfo) == 0) { - auto sourceClient = snd_seq_client_info_get_client (clientInfo); + port = iterateMidiClient (client, clientInfo, forInput, + devices, deviceIdentifierToOpen); - if (sourceClient != client->getId() && sourceClient != SND_SEQ_CLIENT_SYSTEM) - { - port = iterateMidiClient (client, clientInfo, forInput, - deviceNamesFound, deviceIndexToOpen); - if (port != nullptr) - break; - } + if (port != nullptr) + break; } } } } - deviceNamesFound.appendNumbersToDuplicates (true, true); - return port; } } // namespace -StringArray MidiOutput::getDevices() +//============================================================================== +Array MidiInput::getAvailableDevices() { - StringArray devices; - iterateMidiDevices (false, devices, -1); + Array devices; + iterateMidiDevices (true, devices, {}); + return devices; } -int MidiOutput::getDefaultDeviceIndex() +MidiDeviceInfo MidiInput::getDefaultDevice() +{ + return getAvailableDevices().getFirst(); +} + +std::unique_ptr MidiInput::openDevice (const String& deviceIdentifier, MidiInputCallback* callback) +{ + if (deviceIdentifier.isEmpty()) + return {}; + + Array devices; + auto* port = iterateMidiDevices (true, devices, deviceIdentifier); + + if (port == nullptr || ! port->isValid()) + return {}; + + jassert (port->isValid()); + + std::unique_ptr midiInput (new MidiInput (port->portName, deviceIdentifier)); + + port->setupInput (midiInput.get(), callback); + midiInput->internal = port; + + return midiInput; +} + +std::unique_ptr MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback) +{ + auto client = AlsaClient::getInstance(); + auto* port = client->createPort (deviceName, true, true); + + if (port == nullptr || ! port->isValid()) + return {}; + + std::unique_ptr midiInput (new MidiInput (deviceName, getFormattedPortIdentifier (client->getId(), port->portId))); + + port->setupInput (midiInput.get(), callback); + midiInput->internal = port; + + return midiInput; +} + +StringArray MidiInput::getDevices() +{ + StringArray deviceNames; + + for (auto& d : getAvailableDevices()) + deviceNames.add (d.name); + + deviceNames.appendNumbersToDuplicates (true, true); + + return deviceNames; +} + +int MidiInput::getDefaultDeviceIndex() { return 0; } -MidiOutput* MidiOutput::openDevice (int deviceIndex) +std::unique_ptr MidiInput::openDevice (int index, MidiInputCallback* callback) { - MidiOutput* newDevice = nullptr; - - StringArray devices; - auto* port = iterateMidiDevices (false, devices, deviceIndex); - - if (port == nullptr) - return nullptr; - - jassert (port->isValid()); - - newDevice = new MidiOutput (devices [deviceIndex]); - port->setupOutput(); - newDevice->internal = port; - - return newDevice; + return openDevice (getAvailableDevices()[index].identifier, callback); } -MidiOutput* MidiOutput::createNewDevice (const String& deviceName) -{ - MidiOutput* newDevice = nullptr; - auto client = AlsaClient::getInstance(); - auto* port = client->createPort (deviceName, false, true); - jassert (port != nullptr && port->isValid()); - - newDevice = new MidiOutput (deviceName); - port->setupOutput(); - newDevice->internal = port; - - return newDevice; -} - -MidiOutput::~MidiOutput() -{ - stopBackgroundThread(); - AlsaClient::getInstance()->deletePort (static_cast (internal)); -} - -void MidiOutput::sendMessageNow (const MidiMessage& message) -{ - static_cast (internal)->sendMessageNow (message); -} - -//============================================================================== -MidiInput::MidiInput (const String& nm) : name (nm) +MidiInput::MidiInput (const String& deviceName, const String& deviceIdentifier) + : deviceInfo (deviceName, deviceIdentifier) { } @@ -530,68 +549,117 @@ void MidiInput::stop() static_cast (internal)->enableCallback (false); } -int MidiInput::getDefaultDeviceIndex() +//============================================================================== +Array MidiOutput::getAvailableDevices() +{ + Array devices; + iterateMidiDevices (false, devices, {}); + + return devices; +} + +MidiDeviceInfo MidiOutput::getDefaultDevice() +{ + return getAvailableDevices().getFirst(); +} + +std::unique_ptr MidiOutput::openDevice (const String& deviceIdentifier) +{ + if (deviceIdentifier.isEmpty()) + return {}; + + Array devices; + auto* port = iterateMidiDevices (false, devices, deviceIdentifier); + + if (port == nullptr || ! port->isValid()) + return {}; + + std::unique_ptr midiOutput (new MidiOutput (port->portName, deviceIdentifier)); + + port->setupOutput(); + midiOutput->internal = port; + + return midiOutput; +} + +std::unique_ptr MidiOutput::createNewDevice (const String& deviceName) +{ + auto client = AlsaClient::getInstance(); + auto* port = client->createPort (deviceName, false, true); + + if (port == nullptr || ! port->isValid()) + return {}; + + std::unique_ptr midiOutput (new MidiOutput (deviceName, getFormattedPortIdentifier (client->getId(), port->portId))); + + port->setupOutput(); + midiOutput->internal = port; + + return midiOutput; +} + +StringArray MidiOutput::getDevices() +{ + StringArray deviceNames; + + for (auto& d : getAvailableDevices()) + deviceNames.add (d.name); + + deviceNames.appendNumbersToDuplicates (true, true); + + return deviceNames; +} + +int MidiOutput::getDefaultDeviceIndex() { return 0; } -StringArray MidiInput::getDevices() +std::unique_ptr MidiOutput::openDevice (int index) { - StringArray devices; - iterateMidiDevices (true, devices, -1); - return devices; + return openDevice (getAvailableDevices()[index].identifier); } -MidiInput* MidiInput::openDevice (int deviceIndex, MidiInputCallback* callback) +MidiOutput::~MidiOutput() { - StringArray devices; - auto* port = iterateMidiDevices (true, devices, deviceIndex); - - if (port == nullptr) - return nullptr; - - jassert (port->isValid()); - - auto newDevice = new MidiInput (devices [deviceIndex]); - port->setupInput (newDevice, callback); - newDevice->internal = port; - return newDevice; + stopBackgroundThread(); + AlsaClient::getInstance()->deletePort (static_cast (internal)); } -MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback) +void MidiOutput::sendMessageNow (const MidiMessage& message) { - auto client = AlsaClient::getInstance(); - auto* port = client->createPort (deviceName, true, true); - - jassert (port->isValid()); - - auto newDevice = new MidiInput (deviceName); - port->setupInput (newDevice, callback); - newDevice->internal = port; - return newDevice; + static_cast (internal)->sendMessageNow (message); } - //============================================================================== #else // (These are just stub functions if ALSA is unavailable...) +MidiInput::MidiInput (const String& deviceName, const String& deviceID) + : deviceInfo (deviceName, deviceID) +{ +} -StringArray MidiOutput::getDevices() { return {}; } -int MidiOutput::getDefaultDeviceIndex() { return 0; } -MidiOutput* MidiOutput::openDevice (int) { return nullptr; } -MidiOutput* MidiOutput::createNewDevice (const String&) { return nullptr; } -MidiOutput::~MidiOutput() {} -void MidiOutput::sendMessageNow (const MidiMessage&) {} +MidiInput::~MidiInput() {} +void MidiInput::start() {} +void MidiInput::stop() {} +Array MidiInput::getAvailableDevices() { return {}; } +MidiDeviceInfo MidiInput::getDefaultDevice() { return {}; } +std::unique_ptr MidiInput::openDevice (const String&, MidiInputCallback*) { return {}; } +std::unique_ptr MidiInput::createNewDevice (const String&, MidiInputCallback*) { return {}; } +StringArray MidiInput::getDevices() { return {}; } +int MidiInput::getDefaultDeviceIndex() { return 0;} +std::unique_ptr MidiInput::openDevice (int, MidiInputCallback*) { return {}; } -MidiInput::MidiInput (const String& nm) : name (nm) {} -MidiInput::~MidiInput() {} -void MidiInput::start() {} -void MidiInput::stop() {} -int MidiInput::getDefaultDeviceIndex() { return 0; } -StringArray MidiInput::getDevices() { return {}; } -MidiInput* MidiInput::openDevice (int, MidiInputCallback*) { return nullptr; } -MidiInput* MidiInput::createNewDevice (const String&, MidiInputCallback*) { return nullptr; } +MidiOutput::~MidiOutput() {} +void MidiOutput::sendMessageNow (const MidiMessage&) {} +Array MidiOutput::getAvailableDevices() { return {}; } +MidiDeviceInfo MidiOutput::getDefaultDevice() { return {}; } +std::unique_ptr MidiOutput::openDevice (const String&) { return {}; } +std::unique_ptr MidiOutput::createNewDevice (const String&) { return {}; } +StringArray MidiOutput::getDevices() { return {}; } +int MidiOutput::getDefaultDeviceIndex() { return 0;} +std::unique_ptr MidiOutput::openDevice (int) { return {}; } #endif diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp index 4a2634f..86b0640 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp @@ -31,15 +31,7 @@ namespace juce #ifdef __clang__ #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wnonnull" // aovid some spurious 10.11 SDK warnings - - // The AudioHardwareService stuff was deprecated in 10.11 but there's no replacement yet, - // so we'll have to silence the warnings here and revisit it in a future OS version.. - #if ((defined (MAC_OS_X_VERSION_10_13) && MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_13) \ - || (defined (MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_12) \ - || (defined (MAC_OS_X_VERSION_10_11) && MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_11)) - #pragma clang diagnostic ignored "-Wdeprecated-declarations" - #endif + #pragma clang diagnostic ignored "-Wnonnull" // avoid some spurious 10.11 SDK warnings #endif //============================================================================== @@ -52,11 +44,10 @@ struct SystemVol addr.mElement = kAudioObjectPropertyElementMaster; addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - if (AudioHardwareServiceHasProperty (kAudioObjectSystemObject, &addr)) + if (AudioObjectHasProperty (kAudioObjectSystemObject, &addr)) { UInt32 deviceIDSize = sizeof (outputDeviceID); - OSStatus status = AudioHardwareServiceGetPropertyData (kAudioObjectSystemObject, &addr, 0, - nullptr, &deviceIDSize, &outputDeviceID); + OSStatus status = AudioObjectGetPropertyData (kAudioObjectSystemObject, &addr, 0, nullptr, &deviceIDSize, &outputDeviceID); if (status == noErr) { @@ -64,7 +55,7 @@ struct SystemVol addr.mSelector = selector; addr.mScope = kAudioDevicePropertyScopeOutput; - if (! AudioHardwareServiceHasProperty (outputDeviceID, &addr)) + if (! AudioObjectHasProperty (outputDeviceID, &addr)) outputDeviceID = kAudioObjectUnknown; } } @@ -77,8 +68,7 @@ struct SystemVol if (outputDeviceID != kAudioObjectUnknown) { UInt32 size = sizeof (gain); - AudioHardwareServiceGetPropertyData (outputDeviceID, &addr, - 0, nullptr, &size, &gain); + AudioObjectGetPropertyData (outputDeviceID, &addr, 0, nullptr, &size, &gain); } return (float) gain; @@ -91,8 +81,7 @@ struct SystemVol Float32 newVolume = gain; UInt32 size = sizeof (newVolume); - return AudioHardwareServiceSetPropertyData (outputDeviceID, &addr, 0, nullptr, - size, &newVolume) == noErr; + return AudioObjectSetPropertyData (outputDeviceID, &addr, 0, nullptr, size, &newVolume) == noErr; } return false; @@ -105,8 +94,7 @@ struct SystemVol if (outputDeviceID != kAudioObjectUnknown) { UInt32 size = sizeof (muted); - AudioHardwareServiceGetPropertyData (outputDeviceID, &addr, - 0, nullptr, &size, &muted); + AudioObjectGetPropertyData (outputDeviceID, &addr, 0, nullptr, &size, &muted); } return muted != 0; @@ -119,8 +107,7 @@ struct SystemVol UInt32 newMute = mute ? 1 : 0; UInt32 size = sizeof (newMute); - return AudioHardwareServiceSetPropertyData (outputDeviceID, &addr, 0, nullptr, - size, &newMute) == noErr; + return AudioObjectSetPropertyData (outputDeviceID, &addr, 0, nullptr, size, &newMute) == noErr; } return false; @@ -133,8 +120,7 @@ private: bool canSetVolume() const noexcept { Boolean isSettable = NO; - return AudioHardwareServiceIsPropertySettable (outputDeviceID, &addr, &isSettable) == noErr - && isSettable; + return AudioObjectIsPropertySettable (outputDeviceID, &addr, &isSettable) == noErr && isSettable; } }; @@ -547,7 +533,7 @@ public: if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ¤tSourceID))) { HeapBlock types; - const int num = getAllDataSourcesForDevice (deviceID, types); + auto num = getAllDataSourcesForDevice (deviceID, types); for (int i = 0; i < num; ++i) { @@ -568,7 +554,7 @@ public: if (deviceID != 0) { HeapBlock types; - const int num = getAllDataSourcesForDevice (deviceID, types); + auto num = getAllDataSourcesForDevice (deviceID, types); if (isPositiveAndBelow (index, num)) { @@ -816,8 +802,8 @@ public: JUCE_COREAUDIOLOG ("Device changed"); stopTimer(); - const double oldSampleRate = sampleRate; - const int oldBufferSize = bufferSize; + auto oldSampleRate = sampleRate; + auto oldBufferSize = bufferSize; if (! updateDetailsFromDevice()) owner.stopInternal(); @@ -866,15 +852,17 @@ private: return noErr; } - static OSStatus deviceListenerProc (AudioDeviceID /*inDevice*/, UInt32 /*inLine*/, const AudioObjectPropertyAddress* pa, void* inClientData) + static OSStatus deviceListenerProc (AudioDeviceID /*inDevice*/, UInt32 /*inLine*/, + const AudioObjectPropertyAddress* pa, void* inClientData) { - CoreAudioInternal* const intern = static_cast (inClientData); + auto intern = static_cast (inClientData); switch (pa->mSelector) { case kAudioDeviceProcessorOverload: intern->xruns++; break; + case kAudioDevicePropertyBufferSize: case kAudioDevicePropertyBufferFrameSize: case kAudioDevicePropertyNominalSampleRate: @@ -950,8 +938,8 @@ class CoreAudioIODevice : public AudioIODevice, public: CoreAudioIODevice (CoreAudioIODeviceType* dt, const String& deviceName, - AudioDeviceID inputDeviceId, const int inputIndex_, - AudioDeviceID outputDeviceId, const int outputIndex_) + AudioDeviceID inputDeviceId, int inputIndex_, + AudioDeviceID outputDeviceId, int outputIndex_) : AudioIODevice (deviceName, "CoreAudio"), deviceType (dt), inputIndex (inputIndex_), @@ -1027,12 +1015,13 @@ public: inputChannelsRequested = inputChannels; outputChannelsRequested = outputChannels; - sampleRateRequested = sampleRate; - bufferSizeSamplesRequested = bufferSizeSamples; if (bufferSizeSamples <= 0) bufferSizeSamples = getDefaultBufferSize(); + if (sampleRate <= 0) + sampleRate = internal->getNominalSampleRate(); + lastError = internal->reopen (inputChannels, outputChannels, sampleRate, bufferSizeSamples); JUCE_COREAUDIOLOG ("Opened: " << getName()); @@ -1086,7 +1075,7 @@ public: if (isStarted) { - AudioIODeviceCallback* const lastCallback = internal->callback; + auto lastCallback = internal->callback; isStarted = false; internal->stop (true); @@ -1167,8 +1156,6 @@ private: AudioIODeviceCallback* previousCallback = nullptr; std::function deviceWrapperRestartCallback = nullptr; BigInteger inputChannelsRequested, outputChannelsRequested; - double sampleRateRequested; - int bufferSizeSamplesRequested; CriticalSection closeLock; void timerCallback() override diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp index c5beef2..8b1e352 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp @@ -29,6 +29,7 @@ namespace juce namespace CoreMidiHelpers { + //============================================================================== static bool checkError (OSStatus err, int lineNum) { if (err == noErr) @@ -45,79 +46,71 @@ namespace CoreMidiHelpers #undef CHECK_ERROR #define CHECK_ERROR(a) CoreMidiHelpers::checkError (a, __LINE__) - //============================================================================== - struct ScopedCFString + static MidiDeviceInfo getMidiObjectInfo (MIDIObjectRef entity) { - ScopedCFString() noexcept {} - ~ScopedCFString() noexcept { if (cfString != nullptr) CFRelease (cfString); } + MidiDeviceInfo info; - CFStringRef cfString = {}; - }; - - static String getMidiObjectName (MIDIObjectRef entity) - { - String result; - CFStringRef str = nullptr; - MIDIObjectGetStringProperty (entity, kMIDIPropertyName, &str); - - if (str != nullptr) { - result = String::fromCFString (str); - CFRelease (str); + ScopedCFString str; + + if (CHECK_ERROR (MIDIObjectGetStringProperty (entity, kMIDIPropertyName, &str.cfString))) + info.name = String::fromCFString (str.cfString); } - return result; - } + SInt32 objectID = 0; - static void enableSimulatorMidiSession() - { - #if TARGET_OS_SIMULATOR - static bool hasEnabledNetworkSession = false; - - if (! hasEnabledNetworkSession) + if (CHECK_ERROR (MIDIObjectGetIntegerProperty (entity, kMIDIPropertyUniqueID, &objectID))) { - MIDINetworkSession* session = [MIDINetworkSession defaultSession]; - session.enabled = YES; - session.connectionPolicy = MIDINetworkConnectionPolicy_Anyone; - - hasEnabledNetworkSession = true; + info.identifier = String (objectID); } - #endif + else + { + ScopedCFString str; + + if (CHECK_ERROR (MIDIObjectGetStringProperty (entity, kMIDIPropertyUniqueID, &str.cfString))) + info.identifier = String::fromCFString (str.cfString); + } + + return info; } - static String getEndpointName (MIDIEndpointRef endpoint, bool isExternal) + static MidiDeviceInfo getEndpointInfo (MIDIEndpointRef endpoint, bool isExternal) { - auto result = getMidiObjectName (endpoint); - - MIDIEntityRef entity = 0; // NB: don't attempt to use nullptr for refs - it fails in some types of build. + // NB: don't attempt to use nullptr for refs - it fails in some types of build. + MIDIEntityRef entity = 0; MIDIEndpointGetEntity (endpoint, &entity); + // probably virtual if (entity == 0) - return result; // probably virtual + return getMidiObjectInfo (endpoint); - if (result.isEmpty()) - result = getMidiObjectName (entity); // endpoint name is empty - try the entity + auto result = getMidiObjectInfo (endpoint); - // now consider the device's name + // endpoint is empty - try the entity + if (result == MidiDeviceInfo()) + result = getMidiObjectInfo (entity); + + // now consider the device MIDIDeviceRef device = 0; MIDIEntityGetDevice (entity, &device); if (device != 0) { - auto deviceName = getMidiObjectName (device); + auto info = getMidiObjectInfo (device); - if (deviceName.isNotEmpty()) + if (info != MidiDeviceInfo()) { // if an external device has only one entity, throw away // the endpoint name and just use the device name if (isExternal && MIDIDeviceGetNumberOfEntities (device) < 2) { - result = deviceName; + result = info; } - else if (! result.startsWithIgnoreCase (deviceName)) + else if (! result.name.startsWithIgnoreCase (info.name)) { - // prepend the device name to the entity name - result = (deviceName + " " + result).trimEnd(); + // prepend the device name and identifier to the entity's + result.name = (info.name + " " + result.name).trimEnd(); + result.identifier = info.identifier + " " + result.identifier; } } } @@ -125,9 +118,9 @@ namespace CoreMidiHelpers return result; } - static String getConnectedEndpointName (MIDIEndpointRef endpoint) + static MidiDeviceInfo getConnectedEndpointInfo (MIDIEndpointRef endpoint) { - String result; + MidiDeviceInfo result; // Does the endpoint have connections? CFDataRef connections = nullptr; @@ -141,37 +134,38 @@ namespace CoreMidiHelpers if (numConnections > 0) { - auto pid = reinterpret_cast (CFDataGetBytePtr (connections)); + auto* pid = reinterpret_cast (CFDataGetBytePtr (connections)); for (int i = 0; i < numConnections; ++i, ++pid) { - auto uid = (MIDIUniqueID) ByteOrder::swapIfLittleEndian ((uint32) *pid); + auto id = (MIDIUniqueID) ByteOrder::swapIfLittleEndian ((uint32) *pid); MIDIObjectRef connObject; MIDIObjectType connObjectType; - auto err = MIDIObjectFindByUniqueID (uid, &connObject, &connObjectType); + auto err = MIDIObjectFindByUniqueID (id, &connObject, &connObjectType); if (err == noErr) { - String s; + MidiDeviceInfo deviceInfo; if (connObjectType == kMIDIObjectType_ExternalSource || connObjectType == kMIDIObjectType_ExternalDestination) { // Connected to an external device's endpoint (10.3 and later). - s = getEndpointName (static_cast (connObject), true); + deviceInfo = getEndpointInfo (static_cast (connObject), true); } else { // Connected to an external device (10.2) (or something else, catch-all) - s = getMidiObjectName (connObject); + deviceInfo = getMidiObjectInfo (connObject); } - if (s.isNotEmpty()) + if (deviceInfo != MidiDeviceInfo()) { - if (result.isNotEmpty()) - result += ", "; + if (result.name.isNotEmpty()) result.name += ", "; + if (result.identifier.isNotEmpty()) result.identifier += ", "; - result += s; + result.name += deviceInfo.name; + result.identifier += deviceInfo.identifier; } } } @@ -180,22 +174,24 @@ namespace CoreMidiHelpers CFRelease (connections); } - if (result.isEmpty()) // Here, either the endpoint had no connections, or we failed to obtain names for them. - result = getEndpointName (endpoint, false); + // Here, either the endpoint had no connections, or we failed to obtain names for them. + if (result == MidiDeviceInfo()) + return getEndpointInfo (endpoint, false); return result; } - static void setUniqueIdForMidiPort (MIDIObjectRef device, const String& portName, bool isInput) + static int createUniqueIDForMidiPort (String deviceName, bool isInput) { - String portUniqueId; - #if defined (JucePlugin_CFBundleIdentifier) - portUniqueId = JUCE_STRINGIFY (JucePlugin_CFBundleIdentifier); + String uniqueID; + + #ifdef JucePlugin_CFBundleIdentifier + uniqueID = JUCE_STRINGIFY (JucePlugin_CFBundleIdentifier); #else auto appBundle = File::getSpecialLocation (File::currentApplicationFile); + ScopedCFString appBundlePath (appBundle.getFullPathName()); - if (auto bundleURL = CFURLCreateWithFileSystemPath (kCFAllocatorDefault, appBundle.getFullPathName().toCFString(), - kCFURLPOSIXPathStyle, true)) + if (auto bundleURL = CFURLCreateWithFileSystemPath (kCFAllocatorDefault, appBundlePath.cfString, kCFURLPOSIXPathStyle, true)) { auto bundleRef = CFBundleCreate (kCFAllocatorDefault, bundleURL); CFRelease (bundleURL); @@ -203,19 +199,47 @@ namespace CoreMidiHelpers if (bundleRef != nullptr) { if (auto bundleId = CFBundleGetIdentifier (bundleRef)) - portUniqueId = String::fromCFString (bundleId); + uniqueID = String::fromCFString (bundleId); CFRelease (bundleRef); } } #endif - if (portUniqueId.isNotEmpty()) - { - portUniqueId += "." + portName + (isInput ? ".input" : ".output"); + if (uniqueID.isEmpty()) + uniqueID = String (Random::getSystemRandom().nextInt (1024)); - CHECK_ERROR (MIDIObjectSetStringProperty (device, kMIDIPropertyUniqueID, portUniqueId.toCFString())); + uniqueID += "." + deviceName + (isInput ? ".input" : ".output"); + return uniqueID.hashCode(); + } + + static void enableSimulatorMidiSession() + { + #if TARGET_OS_SIMULATOR + static bool hasEnabledNetworkSession = false; + + if (! hasEnabledNetworkSession) + { + auto iOSVersion = nsStringToJuce ([[UIDevice currentDevice] systemVersion]); + auto majorVersion = StringArray::fromTokens (iOSVersion, ".", {})[0].getIntValue(); + + if (majorVersion == 13) + { + // From the Xcode 11 release notes known issues: + // Attempting to create an MIDINetworkSession in a simulated device running + // iOS 13 won’t succeed. (54484923) + jassertfalse; + } + else + { + MIDINetworkSession* session = [MIDINetworkSession defaultSession]; + session.enabled = YES; + session.connectionPolicy = MIDINetworkConnectionPolicy_Anyone; + } + + hasEnabledNetworkSession = true; } + #endif } static void globalSystemChangeCallback (const MIDINotification*, void*) @@ -243,15 +267,14 @@ namespace CoreMidiHelpers enableSimulatorMidiSession(); - CoreMidiHelpers::ScopedCFString name; - name.cfString = getGlobalMidiClientName().toCFString(); + ScopedCFString name (getGlobalMidiClientName()); CHECK_ERROR (MIDIClientCreate (name.cfString, &globalSystemChangeCallback, nullptr, &globalMidiClient)); } return globalMidiClient; } - static StringArray findDevices (bool forInput) + static Array findDevices (bool forInput) { // It seems that OSX can be a bit picky about the thread that's first used to // search for devices. It's safest to use the message thread for calling this. @@ -263,26 +286,25 @@ namespace CoreMidiHelpers return {}; } - StringArray s; enableSimulatorMidiSession(); - auto num = forInput ? MIDIGetNumberOfSources() - : MIDIGetNumberOfDestinations(); + Array devices; + auto numDevices = (forInput ? MIDIGetNumberOfSources() : MIDIGetNumberOfDestinations()); - for (ItemCount i = 0; i < num; ++i) + for (ItemCount i = 0; i < numDevices; ++i) { - String name; + MidiDeviceInfo deviceInfo; if (auto dest = forInput ? MIDIGetSource (i) : MIDIGetDestination (i)) - name = getConnectedEndpointName (dest); + deviceInfo = getConnectedEndpointInfo (dest); - if (name.isEmpty()) - name = ""; + if (deviceInfo == MidiDeviceInfo()) + deviceInfo.name = deviceInfo.identifier = ""; - s.add (name); + devices.add (deviceInfo); } - return s; + return devices; } //============================================================================== @@ -290,7 +312,7 @@ namespace CoreMidiHelpers { public: MidiPortAndEndpoint (MIDIPortRef p, MIDIEndpointRef ep) noexcept - : port (p), endPoint (ep) + : port (p), endpoint (ep) { } @@ -299,20 +321,21 @@ namespace CoreMidiHelpers if (port != 0) MIDIPortDispose (port); - if (port == 0 && endPoint != 0) // if port == nullptr, it means we created the endpoint, so it's safe to delete it - MIDIEndpointDispose (endPoint); + // if port == nullptr, it means we created the endpoint, so it's safe to delete it + if (port == 0 && endpoint != 0) + MIDIEndpointDispose (endpoint); } void send (const MIDIPacketList* packets) noexcept { if (port != 0) - MIDISend (port, endPoint, packets); + MIDISend (port, endpoint, packets); else - MIDIReceived (endPoint, packets); + MIDIReceived (endpoint, packets); } MIDIPortRef port; - MIDIEndpointRef endPoint; + MIDIEndpointRef endpoint; }; //============================================================================== @@ -334,7 +357,7 @@ namespace CoreMidiHelpers } if (portAndEndpoint != nullptr && portAndEndpoint->port != 0) - CHECK_ERROR (MIDIPortDisconnectSource (portAndEndpoint->port, portAndEndpoint->endPoint)); + CHECK_ERROR (MIDIPortDisconnectSource (portAndEndpoint->port, portAndEndpoint->endpoint)); } void handlePackets (const MIDIPacketList* pktlist) @@ -370,61 +393,276 @@ namespace CoreMidiHelpers { static_cast (readProcRefCon)->handlePackets (pktlist); } + + static Array getEndpoints (bool isInput) + { + Array endpoints; + auto numDevices = (isInput ? MIDIGetNumberOfSources() : MIDIGetNumberOfDestinations()); + + for (ItemCount i = 0; i < numDevices; ++i) + endpoints.add (isInput ? MIDIGetSource (i) : MIDIGetDestination (i)); + + return endpoints; + } } //============================================================================== -StringArray MidiOutput::getDevices() { return CoreMidiHelpers::findDevices (false); } -int MidiOutput::getDefaultDeviceIndex() { return 0; } - -MidiOutput* MidiOutput::openDevice (int index) +Array MidiInput::getAvailableDevices() { - MidiOutput* mo = nullptr; + return CoreMidiHelpers::findDevices (true); +} - if (auto client = CoreMidiHelpers::getGlobalMidiClient()) +MidiDeviceInfo MidiInput::getDefaultDevice() +{ + return getAvailableDevices().getFirst(); +} + +std::unique_ptr MidiInput::openDevice (const String& deviceIdentifier, MidiInputCallback* callback) +{ + if (deviceIdentifier.isEmpty()) + return nullptr; + + using namespace CoreMidiHelpers; + + if (auto client = getGlobalMidiClient()) { - if (isPositiveAndBelow (index, MIDIGetNumberOfDestinations())) + for (auto& endpoint : getEndpoints (true)) { - auto endPoint = MIDIGetDestination ((ItemCount) index); + auto endpointInfo = getConnectedEndpointInfo (endpoint); - CoreMidiHelpers::ScopedCFString pname; - - if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname.cfString))) + if (deviceIdentifier == endpointInfo.identifier) { - MIDIPortRef port; - auto deviceName = CoreMidiHelpers::getConnectedEndpointName (endPoint); + ScopedCFString cfName; - if (CHECK_ERROR (MIDIOutputPortCreate (client, pname.cfString, &port))) + if (CHECK_ERROR (MIDIObjectGetStringProperty (endpoint, kMIDIPropertyName, &cfName.cfString))) { - mo = new MidiOutput (deviceName); - mo->internal = new CoreMidiHelpers::MidiPortAndEndpoint (port, endPoint); + MIDIPortRef port; + auto mpc = std::make_unique (*callback); + + if (CHECK_ERROR (MIDIInputPortCreate (client, cfName.cfString, midiInputProc, mpc.get(), &port))) + { + if (CHECK_ERROR (MIDIPortConnectSource (port, endpoint, nullptr))) + { + mpc->portAndEndpoint = std::make_unique (port, endpoint); + + std::unique_ptr midiInput (new MidiInput (endpointInfo.name, endpointInfo.identifier)); + + mpc->input = midiInput.get(); + midiInput->internal = mpc.get(); + + const ScopedLock sl (callbackLock); + activeCallbacks.add (mpc.release()); + + return midiInput; + } + else + { + CHECK_ERROR (MIDIPortDispose (port)); + } + } } } } } - return mo; + return {}; } -MidiOutput* MidiOutput::createNewDevice (const String& deviceName) +std::unique_ptr MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback) { - if (auto client = CoreMidiHelpers::getGlobalMidiClient()) + using namespace CoreMidiHelpers; + jassert (callback != nullptr); + + if (auto client = getGlobalMidiClient()) { - MIDIEndpointRef endPoint; + auto mpc = std::make_unique (*callback); + mpc->active = false; - CoreMidiHelpers::ScopedCFString name; - name.cfString = deviceName.toCFString(); + MIDIEndpointRef endpoint; + ScopedCFString name (deviceName); - if (CHECK_ERROR (MIDISourceCreate (client, name.cfString, &endPoint))) + auto err = MIDIDestinationCreate (client, name.cfString, midiInputProc, mpc.get(), &endpoint); + + #if JUCE_IOS + if (err == kMIDINotPermitted) { - CoreMidiHelpers::setUniqueIdForMidiPort (endPoint, deviceName, false); + // If you've hit this assertion then you probably haven't enabled the "Audio Background Capability" + // setting in the iOS exporter for your app - this is required if you want to create a MIDI device! + jassertfalse; + return nullptr; + } + #endif - auto mo = new MidiOutput (deviceName); - mo->internal = new CoreMidiHelpers::MidiPortAndEndpoint (0, endPoint); - return mo; + if (CHECK_ERROR (err)) + { + auto deviceIdentifier = createUniqueIDForMidiPort (deviceName, true); + + if (CHECK_ERROR (MIDIObjectSetIntegerProperty (endpoint, kMIDIPropertyUniqueID, (SInt32) deviceIdentifier))) + { + mpc->portAndEndpoint = std::make_unique ((UInt32) 0, endpoint); + + std::unique_ptr midiInput (new MidiInput (deviceName, String (deviceIdentifier))); + + mpc->input = midiInput.get(); + midiInput->internal = mpc.get(); + + const ScopedLock sl (callbackLock); + activeCallbacks.add (mpc.release()); + + return midiInput; + } } } - return nullptr; + return {}; +} + +StringArray MidiInput::getDevices() +{ + StringArray deviceNames; + + for (auto& d : getAvailableDevices()) + deviceNames.add (d.name); + + return deviceNames; +} + +int MidiInput::getDefaultDeviceIndex() +{ + return 0; +} + +std::unique_ptr MidiInput::openDevice (int index, MidiInputCallback* callback) +{ + return openDevice (getAvailableDevices()[index].identifier, callback); +} + +MidiInput::MidiInput (const String& deviceName, const String& deviceIdentifier) + : deviceInfo (deviceName, deviceIdentifier) +{ +} + +MidiInput::~MidiInput() +{ + delete static_cast (internal); +} + +void MidiInput::start() +{ + const ScopedLock sl (CoreMidiHelpers::callbackLock); + static_cast (internal)->active = true; +} + +void MidiInput::stop() +{ + const ScopedLock sl (CoreMidiHelpers::callbackLock); + static_cast (internal)->active = false; +} + +//============================================================================== +Array MidiOutput::getAvailableDevices() +{ + return CoreMidiHelpers::findDevices (false); +} + +MidiDeviceInfo MidiOutput::getDefaultDevice() +{ + return getAvailableDevices().getFirst(); +} + +std::unique_ptr MidiOutput::openDevice (const String& deviceIdentifier) +{ + if (deviceIdentifier.isEmpty()) + return nullptr; + + using namespace CoreMidiHelpers; + + if (auto client = getGlobalMidiClient()) + { + for (auto& endpoint : getEndpoints (false)) + { + auto endpointInfo = getConnectedEndpointInfo (endpoint); + + if (deviceIdentifier == endpointInfo.identifier) + { + ScopedCFString cfName; + + if (CHECK_ERROR (MIDIObjectGetStringProperty (endpoint, kMIDIPropertyName, &cfName.cfString))) + { + MIDIPortRef port; + + if (CHECK_ERROR (MIDIOutputPortCreate (client, cfName.cfString, &port))) + { + std::unique_ptr midiOutput (new MidiOutput (endpointInfo.name, endpointInfo.identifier)); + midiOutput->internal = new MidiPortAndEndpoint (port, endpoint); + + return midiOutput; + } + } + } + } + } + + return {}; +} + +std::unique_ptr MidiOutput::createNewDevice (const String& deviceName) +{ + using namespace CoreMidiHelpers; + + if (auto client = getGlobalMidiClient()) + { + MIDIEndpointRef endpoint; + + ScopedCFString name (deviceName); + + auto err = MIDISourceCreate (client, name.cfString, &endpoint); + + #if JUCE_IOS + if (err == kMIDINotPermitted) + { + // If you've hit this assertion then you probably haven't enabled the "Audio Background Capability" + // setting in the iOS exporter for your app - this is required if you want to create a MIDI device! + jassertfalse; + return nullptr; + } + #endif + + if (CHECK_ERROR (err)) + { + auto deviceIdentifier = createUniqueIDForMidiPort (deviceName, false); + + if (CHECK_ERROR (MIDIObjectSetIntegerProperty (endpoint, kMIDIPropertyUniqueID, (SInt32) deviceIdentifier))) + { + std::unique_ptr midiOutput (new MidiOutput (deviceName, String (deviceIdentifier))); + midiOutput->internal = new MidiPortAndEndpoint (0, endpoint); + + return midiOutput; + } + } + } + + return {}; +} + +StringArray MidiOutput::getDevices() +{ + StringArray deviceNames; + + for (auto& d : getAvailableDevices()) + deviceNames.add (d.name); + + return deviceNames; +} + +int MidiOutput::getDefaultDeviceIndex() +{ + return 0; +} + +std::unique_ptr MidiOutput::openDevice (int index) +{ + return openDevice (getAvailableDevices()[index].identifier); } MidiOutput::~MidiOutput() @@ -493,111 +731,6 @@ void MidiOutput::sendMessageNow (const MidiMessage& message) static_cast (internal)->send (packetToSend); } -//============================================================================== -StringArray MidiInput::getDevices() { return CoreMidiHelpers::findDevices (true); } -int MidiInput::getDefaultDeviceIndex() { return 0; } - -MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) -{ - jassert (callback != nullptr); - - using namespace CoreMidiHelpers; - MidiInput* newInput = nullptr; - - if (auto client = getGlobalMidiClient()) - { - if (isPositiveAndBelow (index, MIDIGetNumberOfSources())) - { - if (auto endPoint = MIDIGetSource ((ItemCount) index)) - { - ScopedCFString name; - - if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &name.cfString))) - { - MIDIPortRef port; - std::unique_ptr mpc (new MidiPortAndCallback (*callback)); - - if (CHECK_ERROR (MIDIInputPortCreate (client, name.cfString, midiInputProc, mpc.get(), &port))) - { - if (CHECK_ERROR (MIDIPortConnectSource (port, endPoint, nullptr))) - { - mpc->portAndEndpoint.reset (new MidiPortAndEndpoint (port, endPoint)); - - newInput = new MidiInput (getDevices() [index]); - mpc->input = newInput; - newInput->internal = mpc.get(); - - const ScopedLock sl (callbackLock); - activeCallbacks.add (mpc.release()); - } - else - { - CHECK_ERROR (MIDIPortDispose (port)); - } - } - } - } - } - } - - return newInput; -} - -MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback) -{ - jassert (callback != nullptr); - using namespace CoreMidiHelpers; - - if (auto client = getGlobalMidiClient()) - { - std::unique_ptr mpc (new MidiPortAndCallback (*callback)); - mpc->active = false; - - MIDIEndpointRef endPoint; - ScopedCFString name; - name.cfString = deviceName.toCFString(); - - if (CHECK_ERROR (MIDIDestinationCreate (client, name.cfString, midiInputProc, mpc.get(), &endPoint))) - { - setUniqueIdForMidiPort (endPoint, deviceName, true); - - mpc->portAndEndpoint.reset (new MidiPortAndEndpoint (0, endPoint)); - - auto mi = new MidiInput (deviceName); - mpc->input = mi; - mi->internal = mpc.get(); - - const ScopedLock sl (callbackLock); - activeCallbacks.add (mpc.release()); - - return mi; - } - } - - return nullptr; -} - -MidiInput::MidiInput (const String& nm) : name (nm) -{ -} - -MidiInput::~MidiInput() -{ - delete static_cast (internal); -} - -void MidiInput::start() -{ - const ScopedLock sl (CoreMidiHelpers::callbackLock); - static_cast (internal)->active = true; -} - -void MidiInput::stop() -{ - const ScopedLock sl (CoreMidiHelpers::callbackLock); - static_cast (internal)->active = false; -} - #undef CHECK_ERROR } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp index 1443100..34f438f 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp @@ -43,7 +43,9 @@ namespace ASIODebugging { message = "ASIO: " + message; DBG (message); - Logger::writeToLog (message); + + if (Logger::getCurrentLogger() != nullptr) + Logger::writeToLog (message); } static void logError (const String& context, long error) @@ -336,7 +338,9 @@ public: close(); JUCE_ASIO_LOG ("closed"); - removeCurrentDriver(); + + if (! removeCurrentDriver()) + JUCE_ASIO_LOG ("** Driver crashed while being closed"); } void updateSampleRates() @@ -451,7 +455,9 @@ public: if (needToReset) { JUCE_ASIO_LOG (" Resetting"); - removeCurrentDriver(); + + if (! removeCurrentDriver()) + JUCE_ASIO_LOG ("** Driver crashed while being closed"); loadDriver(); String initError = initDriver(); @@ -459,6 +465,8 @@ public: if (initError.isNotEmpty()) JUCE_ASIO_LOG ("ASIOInit: " + initError); + setSampleRate (getSampleRate()); + needToReset = false; } @@ -638,8 +646,8 @@ public: BigInteger getActiveOutputChannels() const override { return currentChansOut; } BigInteger getActiveInputChannels() const override { return currentChansIn; } - int getOutputLatencyInSamples() override { return outputLatency + currentBlockSizeSamples / 4; } - int getInputLatencyInSamples() override { return inputLatency + currentBlockSizeSamples / 4; } + int getOutputLatencyInSamples() override { return outputLatency; } + int getInputLatencyInSamples() override { return inputLatency; } void start (AudioIODeviceCallback* callback) override { @@ -1073,18 +1081,32 @@ private: } } - void removeCurrentDriver() + bool removeCurrentDriver() { + bool releasedOK = true; + if (asioObject != nullptr) { - asioObject->Release(); + #if ! JUCE_MINGW + __try + #endif + { + asioObject->Release(); + } + #if ! JUCE_MINGW + __except (EXCEPTION_EXECUTE_HANDLER) { releasedOK = false; } + #endif + asioObject = nullptr; } + + return releasedOK; } bool loadDriver() { - removeCurrentDriver(); + if (! removeCurrentDriver()) + JUCE_ASIO_LOG ("** Driver crashed while being closed"); bool crashed = false; bool ok = tryCreatingDriver (crashed); @@ -1125,7 +1147,7 @@ private: if (asioObject == nullptr) return "No Driver"; - const bool initOk = !! asioObject->init (juce_messageWindowHandle); + auto initOk = (asioObject->init (juce_messageWindowHandle) > 0); String driverError; // Get error message if init() failed, or if it's a buggy Denon driver, @@ -1244,7 +1266,9 @@ private: { JUCE_ASIO_LOG_ERROR (error, err); disposeBuffers(); - removeCurrentDriver(); + + if (! removeCurrentDriver()) + JUCE_ASIO_LOG ("** Driver crashed while being closed"); } else { @@ -1566,7 +1590,7 @@ private: //============================================================================== static bool isBlacklistedDriver (const String& driverName) { - return driverName == "ASIO DirectX Full Duplex Driver" || driverName == "ASIO Multimedia Driver"; + return driverName.startsWith ("ASIO DirectX Full Duplex") || driverName == "ASIO Multimedia Driver"; } void addDriverInfo (const String& keyName, HKEY hk) diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp index 914215e..fb6e97f 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp @@ -514,7 +514,6 @@ private: IDirectSoundBuffer* pOutputBuffer; DWORD writeOffset; int totalBytesPerBuffer, bytesPerBuffer; - unsigned int lastPlayCursor; bool firstPlayTime; int64 lastPlayTime, ticksPerBuffer; @@ -1094,7 +1093,7 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, { closeDevice(); - sampleRate = sampleRate_; + sampleRate = sampleRate_ > 0.0 ? sampleRate_ : 44100.0; if (bufferSizeSamples_ <= 0) bufferSizeSamples_ = 960; // use as a default size if none is set. @@ -1215,13 +1214,13 @@ public: initialiseDSoundFunctions(); } - void scanForDevices() + void scanForDevices() override { hasScanned = true; deviceList.scan(); } - StringArray getDeviceNames (bool wantInputNames) const + StringArray getDeviceNames (bool wantInputNames) const override { jassert (hasScanned); // need to call scanForDevices() before doing this @@ -1229,13 +1228,13 @@ public: : deviceList.outputDeviceNames; } - int getDefaultDeviceIndex (bool /*forInput*/) const + int getDefaultDeviceIndex (bool /*forInput*/) const override { jassert (hasScanned); // need to call scanForDevices() before doing this return 0; } - int getIndexOfDevice (AudioIODevice* device, bool asInput) const + int getIndexOfDevice (AudioIODevice* device, bool asInput) const override { jassert (hasScanned); // need to call scanForDevices() before doing this @@ -1246,10 +1245,10 @@ public: return -1; } - bool hasSeparateInputsAndOutputs() const { return true; } + bool hasSeparateInputsAndOutputs() const override { return true; } AudioIODevice* createDevice (const String& outputDeviceName, - const String& inputDeviceName) + const String& inputDeviceName) override { jassert (hasScanned); // need to call scanForDevices() before doing this diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_Midi.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_Midi.cpp index 0864a05..5a39111 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_Midi.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_Midi.cpp @@ -20,6 +20,12 @@ ============================================================================== */ +#ifndef DRV_QUERYDEVICEINTERFACE + #define DRV_RESERVED 0x0800 + #define DRV_QUERYDEVICEINTERFACE (DRV_RESERVED + 12) + #define DRV_QUERYDEVICEINTERFACESIZE (DRV_RESERVED + 13) +#endif + namespace juce { @@ -29,7 +35,9 @@ struct MidiServiceType { virtual ~InputWrapper() {} + virtual String getDeviceIdentifier() = 0; virtual String getDeviceName() = 0; + virtual void start() = 0; virtual void stop() = 0; }; @@ -38,18 +46,20 @@ struct MidiServiceType { virtual ~OutputWrapper() {} + virtual String getDeviceIdentifier() = 0; virtual String getDeviceName() = 0; + virtual void sendMessageNow (const MidiMessage&) = 0; }; MidiServiceType() {} virtual ~MidiServiceType() {} - virtual StringArray getDevices (bool) = 0; - virtual int getDefaultDeviceIndex (bool) = 0; + virtual Array getAvailableDevices (bool) = 0; + virtual MidiDeviceInfo getDefaultDevice (bool) = 0; - virtual InputWrapper* createInputWrapper (MidiInput&, int, MidiInputCallback&) = 0; - virtual OutputWrapper* createOutputWrapper (int) = 0; + virtual InputWrapper* createInputWrapper (MidiInput&, const String&, MidiInputCallback&) = 0; + virtual OutputWrapper* createOutputWrapper (const String&) = 0; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiServiceType) }; @@ -60,26 +70,26 @@ struct Win32MidiService : public MidiServiceType, { Win32MidiService() {} - StringArray getDevices (bool isInput) override + Array getAvailableDevices (bool isInput) override { - return isInput ? Win32InputWrapper::getDevices() - : Win32OutputWrapper::getDevices(); + return isInput ? Win32InputWrapper::getAvailableDevices() + : Win32OutputWrapper::getAvailableDevices(); } - int getDefaultDeviceIndex (bool isInput) override + MidiDeviceInfo getDefaultDevice (bool isInput) override { - return isInput ? Win32InputWrapper::getDefaultDeviceIndex() - : Win32OutputWrapper::getDefaultDeviceIndex(); + return isInput ? Win32InputWrapper::getDefaultDevice() + : Win32OutputWrapper::getDefaultDevice(); } - InputWrapper* createInputWrapper (MidiInput& input, int index, MidiInputCallback& callback) override + InputWrapper* createInputWrapper (MidiInput& input, const String& deviceIdentifier, MidiInputCallback& callback) override { - return new Win32InputWrapper (*this, input, index, callback); + return new Win32InputWrapper (*this, input, deviceIdentifier, callback); } - OutputWrapper* createOutputWrapper (int index) override + OutputWrapper* createOutputWrapper (const String& deviceIdentifier) override { - return new Win32OutputWrapper (*this, index); + return new Win32OutputWrapper (*this, deviceIdentifier); } private: @@ -88,7 +98,10 @@ private: //============================================================================== struct MidiInCollector : public ReferenceCountedObject { - MidiInCollector (Win32MidiService& s, const String& name) : deviceName (name), midiService (s) {} + MidiInCollector (Win32MidiService& s, MidiDeviceInfo d) + : deviceInfo (d), midiService (s) + { + } ~MidiInCollector() { @@ -216,7 +229,7 @@ private: } } - String deviceName; + MidiDeviceInfo deviceInfo; HMIDIIN deviceHandle = 0; private: @@ -319,13 +332,65 @@ private: }; //============================================================================== - struct Win32InputWrapper : public InputWrapper + template + struct Win32MidiDeviceQuery { - Win32InputWrapper (Win32MidiService& parentService, - MidiInput& midiInput, int index, MidiInputCallback& c) + static Array getAvailableDevices() + { + StringArray deviceNames, deviceIDs; + auto deviceCaps = WrapperType::getDeviceCaps(); + + for (int i = 0; i < deviceCaps.size(); ++i) + { + deviceNames.add (deviceCaps[i].szPname); + + auto identifier = getInterfaceIDForDevice ((UINT) i); + + if (identifier.isNotEmpty()) + deviceIDs.add (identifier); + else + deviceIDs.add (deviceNames[i]); + } + + deviceNames.appendNumbersToDuplicates (false, false, CharPointer_UTF8 ("-"), CharPointer_UTF8 ("")); + deviceIDs .appendNumbersToDuplicates (false, false, CharPointer_UTF8 ("-"), CharPointer_UTF8 ("")); + + Array devices; + + for (int i = 0; i < deviceNames.size(); ++i) + devices.add ({ deviceNames[i], deviceIDs[i] }); + + return devices; + } + + private: + static String getInterfaceIDForDevice (UINT id) + { + ULONG size = 0; + + if (WrapperType::sendMidiMessage ((UINT_PTR) id, DRV_QUERYDEVICEINTERFACESIZE, (DWORD_PTR) &size, 0) == MMSYSERR_NOERROR) + { + WCHAR interfaceName[512] = {}; + + if (isPositiveAndBelow (size, sizeof (interfaceName)) + && WrapperType::sendMidiMessage ((UINT_PTR) id, DRV_QUERYDEVICEINTERFACE, + (DWORD_PTR) interfaceName, sizeof (interfaceName)) == MMSYSERR_NOERROR) + { + return interfaceName; + } + } + + return {}; + } + }; + + struct Win32InputWrapper : public InputWrapper, + public Win32MidiDeviceQuery + { + Win32InputWrapper (Win32MidiService& parentService, MidiInput& midiInput, const String& deviceIdentifier, MidiInputCallback& c) : input (midiInput), callback (c) { - collector = getOrCreateCollector (parentService, index); + collector = getOrCreateCollector (parentService, deviceIdentifier); collector->addClient (this); } @@ -334,25 +399,31 @@ private: collector->removeClient (this); } - static MidiInCollector::Ptr getOrCreateCollector (Win32MidiService& parentService, int index) + static MidiInCollector::Ptr getOrCreateCollector (Win32MidiService& parentService, const String& deviceIdentifier) { - auto names = getDevices(); UINT deviceID = MIDI_MAPPER; String deviceName; + auto devices = getAvailableDevices(); - if (isPositiveAndBelow (index, names.size())) + for (int i = 0; i < devices.size(); ++i) { - deviceName = names[index]; - deviceID = index; + auto d = devices.getUnchecked (i); + + if (d.identifier == deviceIdentifier) + { + deviceID = i; + deviceName = d.name; + break; + } } const ScopedLock sl (parentService.activeCollectorLock); for (auto& c : parentService.activeCollectors) - if (c->deviceName == deviceName) + if (c->deviceInfo.identifier == deviceIdentifier) return c; - MidiInCollector::Ptr c (new MidiInCollector (parentService, deviceName)); + MidiInCollector::Ptr c (new MidiInCollector (parentService, { deviceName, deviceIdentifier })); HMIDIIN h; auto err = midiInOpen (&h, deviceID, @@ -368,29 +439,33 @@ private: return c; } - static StringArray getDevices() + static DWORD sendMidiMessage (UINT_PTR deviceID, UINT msg, DWORD_PTR arg1, DWORD_PTR arg2) { - StringArray s; - auto num = midiInGetNumDevs(); + return midiInMessage ((HMIDIIN) deviceID, msg, arg1, arg2); + } - for (UINT i = 0; i < num; ++i) + static Array getDeviceCaps() + { + Array devices; + + for (UINT i = 0; i < midiInGetNumDevs(); ++i) { MIDIINCAPS mc = { 0 }; if (midiInGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR) - s.add (String (mc.szPname, (size_t) numElementsInArray (mc.szPname))); + devices.add (mc); } - s.appendNumbersToDuplicates (false, false, CharPointer_UTF8 ("-"), CharPointer_UTF8 ("")); - return s; + return devices; } - static int getDefaultDeviceIndex() { return 0; } + static MidiDeviceInfo getDefaultDevice() { return getAvailableDevices().getFirst(); } void start() override { started = true; concatenator.reset(); collector->startOrStop(); } void stop() override { started = false; collector->startOrStop(); concatenator.reset(); } - String getDeviceName() override { return collector->deviceName; } + String getDeviceIdentifier() override { return collector->deviceInfo.identifier; } + String getDeviceName() override { return collector->deviceInfo.name; } void pushMidiData (const void* inputData, int numBytes, double time) { @@ -411,8 +486,8 @@ private: { using Ptr = ReferenceCountedObjectPtr; - MidiOutHandle (Win32MidiService& parent, const String& name, HMIDIOUT h) - : owner (parent), deviceName (name), handle (h) + MidiOutHandle (Win32MidiService& parent, MidiDeviceInfo d, HMIDIOUT h) + : owner (parent), deviceInfo (d), handle (h) { owner.activeOutputHandles.add (this); } @@ -426,32 +501,41 @@ private: } Win32MidiService& owner; - String deviceName; + MidiDeviceInfo deviceInfo; HMIDIOUT handle; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutHandle) }; //============================================================================== - struct Win32OutputWrapper : public OutputWrapper + struct Win32OutputWrapper : public OutputWrapper, + public Win32MidiDeviceQuery { - Win32OutputWrapper (Win32MidiService& p, int index) : parent (p) + Win32OutputWrapper (Win32MidiService& p, const String& deviceIdentifier) + : parent (p) { - auto names = getDevices(); + auto devices = getAvailableDevices(); UINT deviceID = MIDI_MAPPER; + String deviceName; - if (isPositiveAndBelow (index, names.size())) + for (int i = 0; i < devices.size(); ++i) { - deviceName = names[index]; - deviceID = index; + auto d = devices.getUnchecked (i); + + if (d.identifier == deviceIdentifier) + { + deviceID = i; + deviceName = d.name; + break; + } } if (deviceID == MIDI_MAPPER) { // use the microsoft sw synth as a default - best not to allow deviceID // to be MIDI_MAPPER, or else device sharing breaks - for (int i = 0; i < names.size(); ++i) - if (names[i].containsIgnoreCase ("microsoft")) + for (int i = 0; i < devices.size(); ++i) + if (devices[i].name.containsIgnoreCase ("microsoft")) deviceID = (UINT) i; } @@ -459,7 +543,7 @@ private: { auto* activeHandle = parent.activeOutputHandles.getUnchecked (i); - if (activeHandle->deviceName == deviceName) + if (activeHandle->deviceInfo.identifier == deviceIdentifier) { han = activeHandle; return; @@ -473,7 +557,7 @@ private: if (res == MMSYSERR_NOERROR) { - han = new MidiOutHandle (parent, deviceName, h); + han = new MidiOutHandle (parent, { deviceName, deviceIdentifier }, h); return; } @@ -530,12 +614,16 @@ private: } } + static DWORD sendMidiMessage (UINT_PTR deviceID, UINT msg, DWORD_PTR arg1, DWORD_PTR arg2) + { + return midiOutMessage ((HMIDIOUT) deviceID, msg, arg1, arg2); + } + static Array getDeviceCaps() { Array devices; - auto num = midiOutGetNumDevs(); - for (UINT i = 0; i < num; ++i) + for (UINT i = 0; i < midiOutGetNumDevs(); ++i) { MIDIOUTCAPS mc = { 0 }; @@ -546,36 +634,26 @@ private: return devices; } - static StringArray getDevices() + static MidiDeviceInfo getDefaultDevice() { - StringArray s; - - for (auto& mc : getDeviceCaps()) - s.add (String (mc.szPname, (size_t) numElementsInArray (mc.szPname))); - - s.appendNumbersToDuplicates (false, false, CharPointer_UTF8 ("-"), CharPointer_UTF8 ("")); - return s; - } - - static int getDefaultDeviceIndex() - { - int n = 0; - - for (auto& mc : getDeviceCaps()) + auto defaultIndex = []() { - if ((mc.wTechnology & MOD_MAPPER) != 0) - return n; + auto deviceCaps = getDeviceCaps(); - ++n; - } + for (int i = 0; i < deviceCaps.size(); ++i) + if ((deviceCaps[i].wTechnology & MOD_MAPPER) != 0) + return i; - return 0; + return 0; + }(); + + return getAvailableDevices()[defaultIndex]; } - String getDeviceName() override { return deviceName; } + String getDeviceIdentifier() override { return han->deviceInfo.identifier; } + String getDeviceName() override { return han->deviceInfo.name; } Win32MidiService& parent; - String deviceName; MidiOutHandle::Ptr han; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Win32OutputWrapper) @@ -638,15 +716,17 @@ public: //============================================================================== WinRTMidiService() { - if (! WinRTWrapper::getInstance()->isInitialised()) + auto* wrtWrapper = WinRTWrapper::getInstance(); + + if (! wrtWrapper->isInitialised()) throw std::runtime_error ("Failed to initialise the WinRT wrapper"); - midiInFactory = WinRTWrapper::getInstance()->getWRLFactory (&RuntimeClass_Windows_Devices_Midi_MidiInPort[0]); + midiInFactory = wrtWrapper->getWRLFactory (&RuntimeClass_Windows_Devices_Midi_MidiInPort[0]); if (midiInFactory == nullptr) throw std::runtime_error ("Failed to create midi in factory"); - midiOutFactory = WinRTWrapper::getInstance()->getWRLFactory (&RuntimeClass_Windows_Devices_Midi_MidiOutPort[0]); + midiOutFactory = wrtWrapper->getWRLFactory (&RuntimeClass_Windows_Devices_Midi_MidiOutPort[0]); if (midiOutFactory == nullptr) throw std::runtime_error ("Failed to create midi out factory"); @@ -671,26 +751,26 @@ public: throw std::runtime_error ("Failed to start the midi output device watcher"); } - StringArray getDevices (bool isInput) override + Array getAvailableDevices (bool isInput) override { - return isInput ? inputDeviceWatcher ->getDevices() - : outputDeviceWatcher->getDevices(); + return isInput ? inputDeviceWatcher ->getAvailableDevices() + : outputDeviceWatcher->getAvailableDevices(); } - int getDefaultDeviceIndex (bool isInput) override + MidiDeviceInfo getDefaultDevice (bool isInput) override { - return isInput ? inputDeviceWatcher ->getDefaultDeviceIndex() - : outputDeviceWatcher->getDefaultDeviceIndex(); + return isInput ? inputDeviceWatcher ->getDefaultDevice() + : outputDeviceWatcher->getDefaultDevice(); } - InputWrapper* createInputWrapper (MidiInput& input, int index, MidiInputCallback& callback) override + InputWrapper* createInputWrapper (MidiInput& input, const String& deviceIdentifier, MidiInputCallback& callback) override { - return new WinRTInputWrapper (*this, input, index, callback); + return new WinRTInputWrapper (*this, input, deviceIdentifier, callback); } - OutputWrapper* createOutputWrapper (int index) override + OutputWrapper* createOutputWrapper (const String& deviceIdentifier) override { - return new WinRTOutputWrapper (*this, index); + return new WinRTOutputWrapper (*this, deviceIdentifier); } private: @@ -706,16 +786,24 @@ private: bool attach (HSTRING deviceSelector, DeviceInformationKind infoKind) { - auto deviceInfoFactory = WinRTWrapper::getInstance()->getWRLFactory (&RuntimeClass_Windows_Devices_Enumeration_DeviceInformation[0]); + auto* wrtWrapper = WinRTWrapper::getInstanceWithoutCreating(); + + if (wrtWrapper == nullptr) + { + JUCE_WINRT_MIDI_LOG ("Failed to get the WinRTWrapper singleton!"); + return false; + } + + auto deviceInfoFactory = wrtWrapper->getWRLFactory (&RuntimeClass_Windows_Devices_Enumeration_DeviceInformation[0]); if (deviceInfoFactory == nullptr) return false; // A quick way of getting an IVector... - auto requestedProperties = [] + auto requestedProperties = [wrtWrapper] { - auto devicePicker = WinRTWrapper::getInstance()->activateInstance (&RuntimeClass_Windows_Devices_Enumeration_DevicePicker[0], - __uuidof (IDevicePicker)); + auto devicePicker = wrtWrapper->activateInstance (&RuntimeClass_Windows_Devices_Enumeration_DevicePicker[0], + __uuidof (IDevicePicker)); jassert (devicePicker != nullptr); IVector* result; @@ -728,9 +816,9 @@ private: return result; }(); - StringArray propertyKeys = { "System.Devices.ContainerId", - "System.Devices.Aep.ContainerId", - "System.Devices.Aep.IsConnected" }; + StringArray propertyKeys ("System.Devices.ContainerId", + "System.Devices.Aep.ContainerId", + "System.Devices.Aep.IsConnected"); for (auto& key : propertyKeys) { @@ -916,7 +1004,7 @@ private: Callback> ( [handlerPtr](IDeviceWatcher*, IDeviceInformationUpdate* infoUpdate) { return handlerPtr->updateDevice (infoUpdate); } ).Get(), - &deviceRemovedToken); + &deviceUpdatedToken); watcher->Start(); } @@ -967,7 +1055,15 @@ private: return S_OK; } - auto deviceID = WinRTWrapper::getInstance()->hStringToString (deviceIDHst); + auto* wrtWrapper = WinRTWrapper::getInstanceWithoutCreating(); + + if (wrtWrapper == nullptr) + { + JUCE_WINRT_MIDI_LOG ("Failed to get the WinRTWrapper singleton!"); + return false; + } + + auto deviceID = wrtWrapper->hStringToString (deviceIDHst); JUCE_WINRT_MIDI_LOG ("Detected paired BLE device: " << deviceID); if (auto* containerIDValue = getValueFromDeviceInfo ("System.Devices.Aep.ContainerId", addedDeviceInfo)) @@ -1004,7 +1100,15 @@ private: return S_OK; } - auto removedDeviceId = WinRTWrapper::getInstance()->hStringToString (removedDeviceIdHstr); + auto* wrtWrapper = WinRTWrapper::getInstanceWithoutCreating(); + + if (wrtWrapper == nullptr) + { + JUCE_WINRT_MIDI_LOG ("Failed to get the WinRTWrapper singleton!"); + return false; + } + + auto removedDeviceId = wrtWrapper->hStringToString (removedDeviceIdHstr); JUCE_WINRT_MIDI_LOG ("Removing BLE device: " << removedDeviceId); @@ -1034,7 +1138,15 @@ private: return S_OK; } - auto updatedDeviceId = WinRTWrapper::getInstance()->hStringToString (updatedDeviceIdHstr); + auto* wrtWrapper = WinRTWrapper::getInstanceWithoutCreating(); + + if (wrtWrapper == nullptr) + { + JUCE_WINRT_MIDI_LOG ("Failed to get the WinRTWrapper singleton!"); + return false; + } + + auto updatedDeviceId = wrtWrapper->hStringToString (updatedDeviceIdHstr); JUCE_WINRT_MIDI_LOG ("Updating BLE device: " << updatedDeviceId); @@ -1098,7 +1210,7 @@ private: }; //============================================================================== - struct MIDIDeviceInfo + struct WinRTMIDIDeviceInfo { String deviceID, containerID, name; bool isDefault = false; @@ -1120,7 +1232,7 @@ private: HRESULT addDevice (IDeviceInformation* addedDeviceInfo) override { - MIDIDeviceInfo info; + WinRTMIDIDeviceInfo info; HSTRING deviceID; auto hr = addedDeviceInfo->get_Id (&deviceID); @@ -1131,7 +1243,15 @@ private: return S_OK; } - info.deviceID = WinRTWrapper::getInstance()->hStringToString (deviceID); + auto* wrtWrapper = WinRTWrapper::getInstanceWithoutCreating(); + + if (wrtWrapper == nullptr) + { + JUCE_WINRT_MIDI_LOG ("Failed to get the WinRTWrapper singleton!"); + return false; + } + + info.deviceID = wrtWrapper->hStringToString (deviceID); JUCE_WINRT_MIDI_LOG ("Detected MIDI device: " << info.deviceID); @@ -1157,7 +1277,7 @@ private: return S_OK; } - info.name = WinRTWrapper::getInstance()->hStringToString (name); + info.name = wrtWrapper->hStringToString (name); boolean isDefault = false; hr = addedDeviceInfo->get_IsDefault (&isDefault); @@ -1191,7 +1311,15 @@ private: return S_OK; } - auto removedDeviceId = WinRTWrapper::getInstance()->hStringToString (removedDeviceIdHstr); + auto* wrtWrapper = WinRTWrapper::getInstanceWithoutCreating(); + + if (wrtWrapper == nullptr) + { + JUCE_WINRT_MIDI_LOG ("Failed to get the WinRTWrapper singleton!"); + return false; + } + + auto removedDeviceId = wrtWrapper->hStringToString (removedDeviceIdHstr); JUCE_WINRT_MIDI_LOG ("Removing MIDI device: " << removedDeviceId); @@ -1229,56 +1357,59 @@ private: return attach (deviceSelector, DeviceInformationKind::DeviceInformationKind_DeviceInterface); } - StringArray getDevices() + Array getAvailableDevices() { { const ScopedLock lock (deviceChanges); lastQueriedConnectedDevices = connectedDevices; } - StringArray result; + StringArray deviceNames, deviceIDs; for (auto info : lastQueriedConnectedDevices.get()) - result.add (info.name); + { + deviceNames.add (info.name); + deviceIDs .add (info.containerID); + } - return result; + deviceNames.appendNumbersToDuplicates (false, false, CharPointer_UTF8 ("-"), CharPointer_UTF8 ("")); + deviceIDs .appendNumbersToDuplicates (false, false, CharPointer_UTF8 ("-"), CharPointer_UTF8 ("")); + + Array devices; + + for (int i = 0; i < deviceNames.size(); ++i) + devices.add ({ deviceNames[i], deviceIDs[i] }); + + return devices; } - int getDefaultDeviceIndex() + MidiDeviceInfo getDefaultDevice() { auto& lastDevices = lastQueriedConnectedDevices.get(); - for (int i = 0; i < lastDevices.size(); ++i) - if (lastDevices[i].isDefault) - return i; - - return 0; - } - - MIDIDeviceInfo getDeviceInfoFromIndex (int index) - { - if (isPositiveAndBelow (index, lastQueriedConnectedDevices.get().size())) - return lastQueriedConnectedDevices.get()[index]; + for (auto& d : lastDevices) + if (d.isDefault) + return { d.name, d.containerID }; return {}; } - String getDeviceID (const String& name) + WinRTMIDIDeviceInfo getWinRTDeviceInfoForDevice (const String& deviceIdentifier) { - const ScopedLock lock (deviceChanges); + auto devices = getAvailableDevices(); - for (auto info : connectedDevices) - if (info.name == name) - return info.deviceID; + for (int i = 0; i < devices.size(); ++i) + if (devices.getUnchecked (i).identifier == deviceIdentifier) + return lastQueriedConnectedDevices.get()[i]; return {}; } WinRTWrapper::ComPtr& factory; - Array connectedDevices; + Array connectedDevices; CriticalSection deviceChanges; - ThreadLocalValue> lastQueriedConnectedDevices; + ThreadLocalValue> lastQueriedConnectedDevices; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiIODeviceWatcher); }; @@ -1327,8 +1458,8 @@ private: } ).Get()); - // We need to use a timout here, rather than waiting indefinitely, as the - // WinRT API can occaisonally hang! + // We need to use a timeout here, rather than waiting indefinitely, as the + // WinRT API can occasionally hang! portOpened.wait (2000); } @@ -1345,12 +1476,12 @@ private: public: WinRTIOWrapper (BLEDeviceWatcher& bleWatcher, MidiIODeviceWatcher& midiDeviceWatcher, - int index) + const String& deviceIdentifier) : bleDeviceWatcher (bleWatcher) { { const ScopedLock lock (midiDeviceWatcher.deviceChanges); - deviceInfo = midiDeviceWatcher.getDeviceInfoFromIndex (index); + deviceInfo = midiDeviceWatcher.getWinRTDeviceInfoForDevice (deviceIdentifier); } if (deviceInfo.deviceID.isEmpty()) @@ -1417,7 +1548,7 @@ private: protected: //============================================================================== BLEDeviceWatcher& bleDeviceWatcher; - MIDIDeviceInfo deviceInfo; + WinRTMIDIDeviceInfo deviceInfo; bool isBLEDevice = false; WinRTWrapper::ComPtr midiPort; }; @@ -1427,8 +1558,8 @@ private: private WinRTIOWrapper { - WinRTInputWrapper (WinRTMidiService& service, MidiInput& input, int index, MidiInputCallback& cb) - : WinRTIOWrapper (*service.bleDeviceWatcher, *service.inputDeviceWatcher, index), + WinRTInputWrapper (WinRTMidiService& service, MidiInput& input, const String& deviceIdentifier, MidiInputCallback& cb) + : WinRTIOWrapper (*service.bleDeviceWatcher, *service.inputDeviceWatcher, deviceIdentifier), inputDevice (input), callback (cb) { @@ -1484,7 +1615,8 @@ private: } } - String getDeviceName() override { return deviceInfo.name; } + String getDeviceIdentifier() override { return deviceInfo.containerID; } + String getDeviceName() override { return deviceInfo.name; } //============================================================================== void disconnect() override @@ -1579,8 +1711,8 @@ private: struct WinRTOutputWrapper final : public OutputWrapper, private WinRTIOWrapper { - WinRTOutputWrapper (WinRTMidiService& service, int index) - : WinRTIOWrapper (*service.bleDeviceWatcher, *service.outputDeviceWatcher, index) + WinRTOutputWrapper (WinRTMidiService& service, const String& deviceIdentifier) + : WinRTIOWrapper (*service.bleDeviceWatcher, *service.outputDeviceWatcher, deviceIdentifier) { OpenMidiPortThread portThread ("Open WinRT MIDI output port", deviceInfo.deviceID, @@ -1592,7 +1724,12 @@ private: if (midiPort == nullptr) throw std::runtime_error ("Timed out waiting for midi output port creation"); - auto bufferFactory = WinRTWrapper::getInstance()->getWRLFactory (&RuntimeClass_Windows_Storage_Streams_Buffer[0]); + auto* wrtWrapper = WinRTWrapper::getInstanceWithoutCreating(); + + if (wrtWrapper == nullptr) + throw std::runtime_error ("Failed to get the WinRTWrapper singleton!"); + + auto bufferFactory = wrtWrapper->getWRLFactory (&RuntimeClass_Windows_Storage_Streams_Buffer[0]); if (bufferFactory == nullptr) throw std::runtime_error ("Failed to create output buffer factory"); @@ -1632,7 +1769,8 @@ private: midiPort->SendBuffer (buffer); } - String getDeviceName() override { return deviceInfo.name; } + String getDeviceIdentifier() override { return deviceInfo.containerID; } + String getDeviceName() override { return deviceInfo.name; } //============================================================================== WinRTWrapper::ComPtr buffer; @@ -1656,33 +1794,17 @@ private: //============================================================================== //============================================================================== +#if ! JUCE_MINGW + extern RTL_OSVERSIONINFOW getWindowsVersionInfo(); +#endif + struct MidiService : public DeletedAtShutdown { MidiService() { - #if JUCE_USE_WINRT_MIDI + #if JUCE_USE_WINRT_MIDI && ! JUCE_MINGW #if ! JUCE_FORCE_WINRT_MIDI - auto windowsVersionInfo = [] - { - RTL_OSVERSIONINFOW versionInfo = { 0 }; - - if (auto* mod = ::GetModuleHandleW (L"ntdll.dll")) - { - using RtlGetVersion = LONG (WINAPI*)(PRTL_OSVERSIONINFOW); - - if (auto* rtlGetVersion = (RtlGetVersion) ::GetProcAddress (mod, "RtlGetVersion")) - { - versionInfo.dwOSVersionInfoSize = sizeof (versionInfo); - LONG STATUS_SUCCESS = 0; - - if (rtlGetVersion (&versionInfo) != STATUS_SUCCESS) - versionInfo = { 0 }; - } - } - - return versionInfo; - }(); - + auto windowsVersionInfo = getWindowsVersionInfo(); if (windowsVersionInfo.dwMajorVersion >= 10 && windowsVersionInfo.dwBuildNumber >= 17763) #endif { @@ -1718,40 +1840,71 @@ private: JUCE_IMPLEMENT_SINGLETON (MidiService) //============================================================================== -StringArray MidiInput::getDevices() +static int findDefaultDeviceIndex (const Array& available, const MidiDeviceInfo& defaultDevice) { - return MidiService::getService().getDevices (true); + for (int i = 0; i < available.size(); ++i) + if (available.getUnchecked (i) == defaultDevice) + return i; + + return 0; } -int MidiInput::getDefaultDeviceIndex() +Array MidiInput::getAvailableDevices() { - return MidiService::getService().getDefaultDeviceIndex (true); + return MidiService::getService().getAvailableDevices (true); } -MidiInput::MidiInput (const String& deviceName) : name (deviceName) +MidiDeviceInfo MidiInput::getDefaultDevice() { + return MidiService::getService().getDefaultDevice (true); } -MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) +std::unique_ptr MidiInput::openDevice (const String& deviceIdentifier, MidiInputCallback* callback) { - if (callback == nullptr) - return nullptr; + if (deviceIdentifier.isEmpty() || callback == nullptr) + return {}; - std::unique_ptr in (new MidiInput (String())); + std::unique_ptr in (new MidiInput ({}, deviceIdentifier)); std::unique_ptr wrapper; try { - wrapper.reset (MidiService::getService().createInputWrapper (*in, index, *callback)); + wrapper.reset (MidiService::getService().createInputWrapper (*in, deviceIdentifier, *callback)); } catch (std::runtime_error&) { - return nullptr; + return {}; } in->setName (wrapper->getDeviceName()); in->internal = wrapper.release(); - return in.release(); + + return in; +} + +StringArray MidiInput::getDevices() +{ + StringArray deviceNames; + + for (auto& d : getAvailableDevices()) + deviceNames.add (d.name); + + return deviceNames; +} + +int MidiInput::getDefaultDeviceIndex() +{ + return findDefaultDeviceIndex (getAvailableDevices(), getDefaultDevice()); +} + +std::unique_ptr MidiInput::openDevice (int index, MidiInputCallback* callback) +{ + return openDevice (getAvailableDevices()[index].identifier, callback); +} + +MidiInput::MidiInput (const String& deviceName, const String& deviceIdentifier) + : deviceInfo (deviceName, deviceIdentifier) +{ } MidiInput::~MidiInput() @@ -1763,32 +1916,58 @@ void MidiInput::start() { static_cast (interna void MidiInput::stop() { static_cast (internal)->stop(); } //============================================================================== -StringArray MidiOutput::getDevices() +Array MidiOutput::getAvailableDevices() { - return MidiService::getService().getDevices (false); + return MidiService::getService().getAvailableDevices (false); } -int MidiOutput::getDefaultDeviceIndex() +MidiDeviceInfo MidiOutput::getDefaultDevice() { - return MidiService::getService().getDefaultDeviceIndex (false); + return MidiService::getService().getDefaultDevice (false); } -MidiOutput* MidiOutput::openDevice (int index) +std::unique_ptr MidiOutput::openDevice (const String& deviceIdentifier) { + if (deviceIdentifier.isEmpty()) + return {}; + std::unique_ptr wrapper; try { - wrapper.reset (MidiService::getService().createOutputWrapper (index)); + wrapper.reset (MidiService::getService().createOutputWrapper (deviceIdentifier)); } catch (std::runtime_error&) { - return nullptr; + return {}; } - std::unique_ptr out (new MidiOutput (wrapper->getDeviceName())); + std::unique_ptr out; + out.reset (new MidiOutput (wrapper->getDeviceName(), deviceIdentifier)); + out->internal = wrapper.release(); - return out.release(); + + return out; +} + +StringArray MidiOutput::getDevices() +{ + StringArray deviceNames; + + for (auto& d : getAvailableDevices()) + deviceNames.add (d.name); + + return deviceNames; +} + +int MidiOutput::getDefaultDeviceIndex() +{ + return findDefaultDeviceIndex (getAvailableDevices(), getDefaultDevice()); +} + +std::unique_ptr MidiOutput::openDevice (int index) +{ + return openDevice (getAvailableDevices()[index].identifier); } MidiOutput::~MidiOutput() diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp index c6f3f0a..d7de74c 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp @@ -454,6 +454,10 @@ public: if (client != nullptr) client->Stop(); + // N.B. this is needed to prevent a double-deletion of the IAudioSessionEvents object + // on older versions of Windows + Thread::sleep (5); + deleteSessionEventCallback(); client = nullptr; ResetEvent (clientEvent); @@ -1016,7 +1020,9 @@ public: sampleRates = d->rates; } + bufferSizes.clear(); bufferSizes.addUsingDefaultSort (defaultBufferSize); + if (minBufferSize != defaultBufferSize) bufferSizes.addUsingDefaultSort (minBufferSize); @@ -1439,7 +1445,7 @@ public: } //============================================================================== - void scanForDevices() + void scanForDevices() override { hasScanned = true; @@ -1452,7 +1458,7 @@ public: outputDeviceIds, inputDeviceIds); } - StringArray getDeviceNames (bool wantInputNames) const + StringArray getDeviceNames (bool wantInputNames) const override { jassert (hasScanned); // need to call scanForDevices() before doing this @@ -1460,13 +1466,13 @@ public: : outputDeviceNames; } - int getDefaultDeviceIndex (bool /*forInput*/) const + int getDefaultDeviceIndex (bool /*forInput*/) const override { jassert (hasScanned); // need to call scanForDevices() before doing this return 0; } - int getIndexOfDevice (AudioIODevice* device, bool asInput) const + int getIndexOfDevice (AudioIODevice* device, bool asInput) const override { jassert (hasScanned); // need to call scanForDevices() before doing this @@ -1477,10 +1483,10 @@ public: return -1; } - bool hasSeparateInputsAndOutputs() const { return true; } + bool hasSeparateInputsAndOutputs() const override { return true; } AudioIODevice* createDevice (const String& outputDeviceName, - const String& inputDeviceName) + const String& inputDeviceName) override { jassert (hasScanned); // need to call scanForDevices() before doing this @@ -1572,9 +1578,6 @@ private: StringArray& outDeviceIds, StringArray& inDeviceIds) { - // Init COM on thread (WASAPI won't work with libopenshot-audio without this line) - CoInitialize(0); - if (enumerator == nullptr) { if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator)))) diff --git a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.cpp b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.cpp index 8e055af..6483363 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.cpp @@ -104,14 +104,14 @@ void AudioSourcePlayer::audioDeviceIOCallback (const float** inputChannelData, for (int i = 0; i < numOutputs; ++i) { channels[numActiveChans] = outputChans[i]; - memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * (size_t) numSamples); + memcpy (channels[numActiveChans], inputChans[i], (size_t) numSamples * sizeof (float)); ++numActiveChans; } for (int i = numOutputs; i < numInputs; ++i) { channels[numActiveChans] = tempBuffer.getWritePointer (i - numOutputs); - memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * (size_t) numSamples); + memcpy (channels[numActiveChans], inputChans[i], (size_t) numSamples * sizeof (float)); ++numActiveChans; } } @@ -120,14 +120,14 @@ void AudioSourcePlayer::audioDeviceIOCallback (const float** inputChannelData, for (int i = 0; i < numInputs; ++i) { channels[numActiveChans] = outputChans[i]; - memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * (size_t) numSamples); + memcpy (channels[numActiveChans], inputChans[i], (size_t) numSamples * sizeof (float)); ++numActiveChans; } for (int i = numInputs; i < numOutputs; ++i) { channels[numActiveChans] = outputChans[i]; - zeromem (channels[numActiveChans], sizeof (float) * (size_t) numSamples); + zeromem (channels[numActiveChans], (size_t) numSamples * sizeof (float)); ++numActiveChans; } } @@ -146,7 +146,7 @@ void AudioSourcePlayer::audioDeviceIOCallback (const float** inputChannelData, { for (int i = 0; i < totalNumOutputChannels; ++i) if (outputChannelData[i] != nullptr) - zeromem (outputChannelData[i], sizeof (float) * (size_t) numSamples); + zeromem (outputChannelData[i], (size_t) numSamples * sizeof (float)); } } diff --git a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.h b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.h index 3f5a87d..f135602 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.h +++ b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.h @@ -105,7 +105,8 @@ private: float* outputChans[128]; const float* inputChans[128]; AudioBuffer tempBuffer; - float lastGain = 1.0f, gain = 1.0f; + float lastGain = 1.0f; + std::atomic gain { 1.0f }; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSourcePlayer) }; diff --git a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp index 34e0574..57c4c87 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp @@ -125,10 +125,7 @@ void AudioTransportSource::stop() { if (playing) { - { - const ScopedLock sl (callbackLock); - playing = false; - } + playing = false; int n = 500; while (--n >= 0 && ! stopped) diff --git a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h index 5e4c212..67c4869 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h +++ b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h @@ -167,7 +167,7 @@ private: CriticalSection callbackLock; float gain = 1.0f, lastGain = 1.0f; - bool playing = false, stopped = true; + std::atomic playing { false }, stopped { true }; double sampleRate = 44100.0, sourceSampleRate = 0; int blockSize = 128, readAheadBufferSize = 0; bool isPrepared = false, inputStreamEOF = false; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp index b866b8a..18f6511 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp @@ -600,17 +600,17 @@ public: } template - static void copySampleData (unsigned int bitsPerSample, bool usesFloatingPointData, + static void copySampleData (unsigned int numBitsPerSample, bool floatingPointData, int* const* destSamples, int startOffsetInDestBuffer, int numDestChannels, - const void* sourceData, int numChannels, int numSamples) noexcept + const void* sourceData, int numberOfChannels, int numSamples) noexcept { - switch (bitsPerSample) + switch (numBitsPerSample) { - case 8: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; - case 16: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; - case 24: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; - case 32: if (usesFloatingPointData) ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); - else ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); + case 8: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples); break; + case 16: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples); break; + case 24: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples); break; + case 32: if (floatingPointData) ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples); + else ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples); break; default: jassertfalse; break; } @@ -846,7 +846,7 @@ public: { jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. - zeromem (result, sizeof (float) * (size_t) num); + zeromem (result, (size_t) num * sizeof (float)); return; } @@ -907,6 +907,8 @@ public: } } + using AudioFormatReader::readMaxLevels; + private: const bool littleEndian; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.h index 2878611..02138ea 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.h @@ -86,6 +86,7 @@ public: int bitsPerSample, const StringPairArray& metadataValues, int qualityOptionIndex) override; + using AudioFormat::createWriterFor; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AiffAudioFormat) diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp index ce4f637..dd6737a 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp @@ -352,9 +352,9 @@ public: auto status = AudioFileOpenWithCallbacks (this, &readCallback, - nullptr, // write needs to be null to avoid permisisions errors + nullptr, // write needs to be null to avoid permissions errors &getSizeCallback, - nullptr, // setSize needs to be null to avoid permisisions errors + nullptr, // setSize needs to be null to avoid permissions errors 0, // AudioFileTypeID inFileTypeHint &audioFileID); if (status == noErr) @@ -475,7 +475,7 @@ public: while (numSamples > 0) { auto numThisTime = jmin (8192, numSamples); - auto numBytes = sizeof (float) * (size_t) numThisTime; + auto numBytes = (size_t) numThisTime * sizeof (float); audioDataBlock.ensureSize (numBytes * numChannels, false); auto* data = static_cast (audioDataBlock.getData()); @@ -494,6 +494,15 @@ public: if (status != noErr) return false; + if (numFramesToRead == 0) + break; + + if ((int) numFramesToRead < numThisTime) + { + numThisTime = (int) numFramesToRead; + numBytes = (size_t) numThisTime * sizeof (float); + } + for (int i = numDestChannels; --i >= 0;) { auto* dest = destSamples[(i < (int) numChannels ? channelMap[i] : i)]; @@ -592,8 +601,8 @@ AudioFormatWriter* CoreAudioFormat::createWriterFor (OutputStream*, return nullptr; } + //============================================================================== -// Unit tests for Core Audio layout conversions //============================================================================== #if JUCE_UNIT_TESTS @@ -603,9 +612,11 @@ AudioFormatWriter* CoreAudioFormat::createWriterFor (OutputStream*, class CoreAudioLayoutsUnitTest : public UnitTest { public: - CoreAudioLayoutsUnitTest() : UnitTest ("Core Audio Layout <-> JUCE channel layout conversion", "Audio") {} + CoreAudioLayoutsUnitTest() + : UnitTest ("Core Audio Layout <-> JUCE channel layout conversion", UnitTestCategories::audio) + {} - // some ambisonic tags which are not explicitely defined + // some ambisonic tags which are not explicitly defined enum { kAudioChannelLayoutTag_HOA_ACN_SN3D_0Order = (190U<<16) | 1, diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.h index c2ed22a..5279801 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.h @@ -76,6 +76,7 @@ public: int bitsPerSample, const StringPairArray& metadataValues, int qualityOptionIndex) override; + using AudioFormat::createWriterFor; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioFormat) diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp index 7d50373..33453b8 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp @@ -122,6 +122,13 @@ namespace FlacNamespace #endif #endif + #if JUCE_GCC + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" + #pragma GCC diagnostic ignored "-Wconversion" + #pragma GCC diagnostic ignored "-Wsign-conversion" + #endif + #if JUCE_INTEL #if JUCE_32BIT #define FLAC__CPU_IA32 1 @@ -161,6 +168,10 @@ namespace FlacNamespace #if JUCE_CLANG #pragma clang diagnostic pop #endif + + #if JUCE_GCC + #pragma GCC diagnostic pop + #endif } #undef max @@ -240,7 +251,7 @@ public: if (destSamples[i] != nullptr) memcpy (destSamples[i] + startOffsetInDestBuffer, reservoir.getReadPointer (i, (int) (startSampleInFile - reservoirStart)), - sizeof (int) * (size_t) num); + (size_t) num * sizeof (int)); startOffsetInDestBuffer += num; startSampleInFile += num; @@ -277,7 +288,7 @@ public: { for (int i = numDestChannels; --i >= 0;) if (destSamples[i] != nullptr) - zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples); + zeromem (destSamples[i] + startOffsetInDestBuffer, (size_t) numSamples * sizeof (int)); } return true; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.h index bee5919..ae0f203 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.h @@ -64,6 +64,8 @@ public: int bitsPerSample, const StringPairArray& metadataValues, int qualityOptionIndex) override; + using AudioFormat::createWriterFor; + private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlacAudioFormat) }; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.h index 4260570..47b0f68 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.h @@ -67,6 +67,7 @@ public: AudioFormatWriter* createWriterFor (OutputStream*, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& metadataValues, int qualityOptionIndex); + using AudioFormat::createWriterFor; private: File lameApp; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp index 14dc9d1..93cd37f 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp @@ -496,8 +496,8 @@ struct MP3Frame mpeg25 = (header & (1 << 20)) == 0; lsf = mpeg25 ? 1 : ((header & (1 << 19)) ? 0 : 1); - layer = 4 - ((header >> 17) & 3); - sampleRateIndex = mpeg25 ? (6 + ((header >> 10) & 3)) : ((int) ((header >> 10) & 3) + (lsf * 3)); + layer = (int) (4 - ((header >> 17) & 3)); + sampleRateIndex = (int) ((header >> 10) & 3) + (mpeg25 ? 6 : (lsf * 3)); crc16FollowsHeader = ((header >> 16) & 1) == 0; bitrateIndex = (header >> 12) & 15; padding = (header >> 9) & 1; @@ -1625,7 +1625,7 @@ private: static bool isValidHeader (uint32 header, int oldLayer) noexcept { - int newLayer = 4 - ((header >> 17) & 3); + auto newLayer = (int) (4 - ((header >> 17) & 3)); return (header & 0xffe00000) == 0xffe00000 && newLayer != 4 @@ -1655,8 +1655,8 @@ private: if (numBits <= 0 || bufferPointer == nullptr) return 0; - const uint32 result = ((((((bufferPointer[0] << 8) | bufferPointer[1]) << 8) - | bufferPointer[2]) << bitIndex) & 0xffffff) >> (24 - numBits); + const auto result = (uint32) (((((((bufferPointer[0] << 8) | bufferPointer[1]) << 8) + | bufferPointer[2]) << bitIndex) & 0xffffff) >> (24 - numBits)); bitIndex += numBits; bufferPointer += (bitIndex >> 3); bitIndex &= 7; @@ -1669,12 +1669,12 @@ private: ++bitIndex; bufferPointer += (bitIndex >> 3); bitIndex &= 7; - return result >> 7; + return (uint32) (result >> 7); } uint32 getBitsUnchecked (int numBits) noexcept { - const uint32 result = ((((bufferPointer[0] << 8) | bufferPointer[1]) << bitIndex) & 0xffff) >> (16 - numBits); + const auto result = (uint32) (((((bufferPointer[0] << 8) | bufferPointer[1]) << bitIndex) & 0xffff) >> (16 - numBits)); bitIndex += numBits; bufferPointer += (bitIndex >> 3); bitIndex &= 7; @@ -1912,9 +1912,10 @@ private: getLayer3SideInfo2 (numChannels, msStereo, sampleRate, single); int databits = 0; + for (int gr = 0; gr < granules; ++gr) for (int ch = 0; ch < numChannels; ++ch) - databits += sideinfo.ch[ch].gr[gr].part2_3Length; + databits += (int) sideinfo.ch[ch].gr[gr].part2_3Length; return databits - 8 * (int) sideinfo.mainDataStart; } @@ -2452,7 +2453,7 @@ private: auto* xrpnt = (float*) xr; auto part2remain = (int) granule.part2_3Length - part2bits; - zeromem (xrpnt, sizeof (float) * (size_t) (&xr[32][0] - xrpnt)); + zeromem (xrpnt, (size_t) (&xr[32][0] - xrpnt) * sizeof (float)); auto bv = (int) granule.bigValues; auto region1 = (int) granule.region1Start; @@ -2549,8 +2550,8 @@ private: if (x == 15) { max[lwin] = cb; - part2remain -= h->bits + 1; - x += getBits ((int) h->bits); + part2remain -= (int) (h->bits + 1); + x += (int) getBits ((int) h->bits); *xrpnt = constants.nToThe4Over3[x] * (getOneBit() ? -v : v); } else if (x) @@ -2567,8 +2568,8 @@ private: if (y == 15) { max[lwin] = cb; - part2remain -= h->bits + 1; - y += getBits ((int) h->bits); + part2remain -= (int) (h->bits + 1); + y += (int) getBits ((int) h->bits); *xrpnt = constants.nToThe4Over3[y] * (getOneBit() ? -v : v); } else if (y) @@ -2709,8 +2710,8 @@ private: if (x == 15) { max = cb; - part2remain -= h->bits + 1; - x += getBits ((int) h->bits); + part2remain -= (int) (h->bits + 1); + x += (int) getBits ((int) h->bits); *xrpnt++ = constants.nToThe4Over3[x] * (getOneBit() ? -v : v); } else if (x) @@ -2725,8 +2726,8 @@ private: if (y == 15) { max = cb; - part2remain -= h->bits + 1; - y += getBits ((int) h->bits); + part2remain -= (int) (h->bits + 1); + y += (int) getBits ((int) h->bits); *xrpnt++ = constants.nToThe4Over3[y] * (getOneBit() ? -v : v); } else if (y) @@ -2788,7 +2789,7 @@ private: } } - zeromem (xrpnt, sizeof (float) * (size_t) (&xr[32][0] - xrpnt)); + zeromem (xrpnt, (size_t) (&xr[32][0] - xrpnt) * sizeof (float)); granule.maxBandl = (uint32) (max + 1); granule.maxb = (uint32) constants.longLimit[sampleRate][granule.maxBandl]; @@ -3006,17 +3007,17 @@ public: { for (int i = numDestChannels; --i >= 0;) if (destSamples[i] != nullptr) - zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (float) * (size_t) numSamples); + zeromem (destSamples[i] + startOffsetInDestBuffer, (size_t) numSamples * sizeof (float)); return false; } const int numToCopy = jmin (decodedEnd - decodedStart, numSamples); float* const* const dst = reinterpret_cast (destSamples); - memcpy (dst[0] + startOffsetInDestBuffer, decoded0 + decodedStart, sizeof (float) * (size_t) numToCopy); + memcpy (dst[0] + startOffsetInDestBuffer, decoded0 + decodedStart, (size_t) numToCopy * sizeof (float)); if (numDestChannels > 1 && dst[1] != nullptr) - memcpy (dst[1] + startOffsetInDestBuffer, (numChannels < 2 ? decoded0 : decoded1) + decodedStart, sizeof (float) * (size_t) numToCopy); + memcpy (dst[1] + startOffsetInDestBuffer, (numChannels < 2 ? decoded0 : decoded1) + decodedStart, (size_t) numToCopy * sizeof (float)); startOffsetInDestBuffer += numToCopy; decodedStart += numToCopy; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.h index b064665..fee0840 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.h @@ -50,7 +50,7 @@ class MP3AudioFormat : public AudioFormat public: //============================================================================== MP3AudioFormat(); - ~MP3AudioFormat(); + ~MP3AudioFormat() override; //============================================================================== Array getPossibleSampleRates() override; @@ -66,6 +66,7 @@ public: AudioFormatWriter* createWriterFor (OutputStream*, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& metadataValues, int qualityOptionIndex) override; + using AudioFormat::createWriterFor; }; #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp index 75bbd3b..f138d8c 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp @@ -49,7 +49,10 @@ namespace OggVorbisNamespace #endif #elif JUCE_GCC #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wconversion" #pragma GCC diagnostic ignored "-Wshadow" + #pragma GCC diagnostic ignored "-Wsign-conversion" + #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" #endif #include "oggvorbis/vorbisenc.h" @@ -178,7 +181,7 @@ public: if (destSamples[i] != nullptr) memcpy (destSamples[i] + startOffsetInDestBuffer, reservoir.getReadPointer (i, (int) (startSampleInFile - reservoirStart)), - sizeof (float) * (size_t) numToUse); + (size_t) numToUse * sizeof (float)); startSampleInFile += numToUse; numSamples -= numToUse; @@ -213,7 +216,7 @@ public: jassert (samps <= numToRead); for (int i = jmin ((int) numChannels, reservoir.getNumChannels()); --i >= 0;) - memcpy (reservoir.getWritePointer (i, offset), dataIn[i], sizeof (float) * (size_t) samps); + memcpy (reservoir.getWritePointer (i, offset), dataIn[i], (size_t) samps * sizeof (float)); numToRead -= samps; offset += samps; @@ -228,7 +231,7 @@ public: { for (int i = numDestChannels; --i >= 0;) if (destSamples[i] != nullptr) - zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples); + zeromem (destSamples[i] + startOffsetInDestBuffer, (size_t) numSamples * sizeof (int)); } return true; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.h index 2337f3c..7264e2d 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.h @@ -91,6 +91,7 @@ public: int bitsPerSample, const StringPairArray& metadataValues, int qualityOptionIndex) override; + using AudioFormat::createWriterFor; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggVorbisAudioFormat) diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp index 6ca243b..3515786 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp @@ -1001,7 +1001,7 @@ public: } else { - bytesPerFrame = numChannels * bitsPerSample / 8; + bytesPerFrame = (int) (numChannels * bitsPerSample / 8); } if (format == 3) @@ -1052,8 +1052,15 @@ public: } else if (chunkType == chunkName ("data")) { - if (! isRF64) // data size is expected to be -1, actual data size is in ds64 chunk + if (isRF64) + { + if (dataLength > 0) + chunkEnd = input->getPosition() + dataLength + (dataLength & 1); + } + else + { dataLength = length; + } dataChunkStart = input->getPosition(); lengthInSamples = (bytesPerFrame > 0) ? (dataLength / bytesPerFrame) : 0; @@ -1220,17 +1227,17 @@ public: return true; } - static void copySampleData (unsigned int bitsPerSample, const bool usesFloatingPointData, + static void copySampleData (unsigned int numBitsPerSample, const bool floatingPointData, int* const* destSamples, int startOffsetInDestBuffer, int numDestChannels, - const void* sourceData, int numChannels, int numSamples) noexcept + const void* sourceData, int numberOfChannels, int numSamples) noexcept { - switch (bitsPerSample) + switch (numBitsPerSample) { - case 8: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; - case 16: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; - case 24: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; - case 32: if (usesFloatingPointData) ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); - else ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); + case 8: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples); break; + case 16: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples); break; + case 24: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples); break; + case 32: if (floatingPointData) ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples); + else ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numberOfChannels, numSamples); break; default: jassertfalse; break; } @@ -1349,7 +1356,7 @@ public: { // failed to write to disk, so let's try writing the header. // If it's just run out of disk space, then if it does manage - // to write the header, we'll still have a useable file.. + // to write the header, we'll still have a usable file.. writeHeader(); writeFailed = true; return false; @@ -1518,17 +1525,17 @@ private: } } - static int getChannelMaskFromChannelLayout (const AudioChannelSet& channelLayout) + static int getChannelMaskFromChannelLayout (const AudioChannelSet& layout) { - if (channelLayout.isDiscreteLayout()) + if (layout.isDiscreteLayout()) return 0; // Don't add an extended format chunk for mono and stereo. Basically, all wav players // interpret a wav file with only one or two channels to be mono or stereo anyway. - if (channelLayout == AudioChannelSet::mono() || channelLayout == AudioChannelSet::stereo()) + if (layout == AudioChannelSet::mono() || layout == AudioChannelSet::stereo()) return 0; - auto channels = channelLayout.getChannelTypes(); + auto channels = layout.getChannelTypes(); auto wavChannelMask = 0; for (auto channel : channels) @@ -1581,7 +1588,7 @@ public: { jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. - zeromem (result, sizeof (float) * (size_t) num); + zeromem (result, (size_t) num * sizeof (float)); return; } @@ -1626,6 +1633,8 @@ public: } } + using AudioFormatReader::readMaxLevels; + private: template void scanMinAndMax (int64 startSampleInFile, int64 numSamples, Range* results, int numChannelsToRead) const noexcept @@ -1809,12 +1818,16 @@ bool WavAudioFormat::replaceMetadataInFile (const File& wavFile, const StringPai return slowCopyWavFileWithNewMetadata (wavFile, newMetadata); } + +//============================================================================== //============================================================================== #if JUCE_UNIT_TESTS struct WaveAudioFormatTests : public UnitTest { - WaveAudioFormatTests() : UnitTest ("Wave audio format tests") {} + WaveAudioFormatTests() + : UnitTest ("Wave audio format tests", UnitTestCategories::audio) + {} void runTest() override { diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h index 9643cf3..46394be 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h @@ -210,6 +210,7 @@ public: int bitsPerSample, const StringPairArray& metadataValues, int qualityOptionIndex) override; + using AudioFormat::createWriterFor; //============================================================================== /** Utility function to replace the metadata in a wav file with a new set of values. diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp index 27803e0..3474442 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp @@ -30,11 +30,11 @@ namespace juce namespace WindowsMediaCodec { -class JuceIStream : public ComBaseClassHelper +class JuceIStream : public ComBaseClassHelper { public: JuceIStream (InputStream& in) noexcept - : ComBaseClassHelper (0), source (in) + : ComBaseClassHelper (0), source (in) { } @@ -48,10 +48,10 @@ public: JUCE_COMRESULT Read (void* dest, ULONG numBytes, ULONG* bytesRead) { - auto numRead = source.read (dest, numBytes); + auto numRead = source.read (dest, (size_t) numBytes); if (bytesRead != nullptr) - *bytesRead = numRead; + *bytesRead = (ULONG) numRead; return (numRead == (int) numBytes) ? S_OK : S_FALSE; } @@ -206,7 +206,7 @@ public: if (hasJumped) bufferedRange.setStart ((int64) ((sampleTime * (int64) sampleRate) / 10000000)); else - bufferedRange.setStart (bufferedRange.getEnd()); // (because the positions returned often aren't continguous) + bufferedRange.setStart (bufferedRange.getEnd()); // (because the positions returned often aren't contiguous) bufferedRange.setLength ((int64) (dataLength / stride)); diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.h index 1b600c5..e2fe126 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.h @@ -55,6 +55,7 @@ public: AudioFormatWriter* createWriterFor (OutputStream*, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& metadataValues, int qualityOptionIndex) override; + using AudioFormat::createWriterFor; }; #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/os.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/os.h index 412ca76..4997d65 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/os.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/os.h @@ -145,7 +145,7 @@ static __inline void vorbis_fpu_restore(vorbis_fpu_control fpu){ /* Optimized code path for x86_64 builds. Uses SSE2 intrinsics. This can be done safely because all x86_64 CPUs supports SSE2. */ -#if (! JUCE_PROJUCER_LIVE_BUILD) && ((defined(_MSC_VER) && defined(_WIN64)) || (defined(__GNUC__) && defined (__x86_64__))) +#if ! JUCE_PROJUCER_LIVE_BUILD && ((JUCE_MSVC && JUCE_64BIT) || (JUCE_GCC && defined (__x86_64__))) # define VORBIS_FPU_CONTROL typedef ogg_int16_t vorbis_fpu_control; diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.h index dd6bacc..d300857 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.h @@ -132,9 +132,7 @@ public: to try to open a different format, etc @param sampleRateToUse the sample rate for the file, which must be one of the ones returned by getPossibleSampleRates() - @param numberOfChannels the number of channels - this must be either 1 or 2, and - the choice will depend on the results of canDoMono() and - canDoStereo() + @param numberOfChannels the number of channels @param bitsPerSample the bits per sample to use - this must be one of the values returned by getPossibleBitDepths() @param metadataValues a set of metadata values that the writer should try to write diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatManager.h b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatManager.h index 1a618aa..36140e8 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatManager.h +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatManager.h @@ -66,7 +66,8 @@ public: bool makeThisTheDefaultFormat); /** Handy method to make it easy to register the formats that come with JUCE. - Currently, this will add WAV and AIFF to the list. + This will add WAV and AIFF to the list, along with any other formats enabled + in either the Projucer or your application's AppConfig.h. */ void registerBasicFormats(); @@ -80,10 +81,16 @@ public: AudioFormat* getKnownFormat (int index) const; /** Iterator access to the list of known formats. */ - AudioFormat** begin() const noexcept { return knownFormats.begin(); } + AudioFormat** begin() noexcept { return knownFormats.begin(); } /** Iterator access to the list of known formats. */ - AudioFormat** end() const noexcept { return knownFormats.end(); } + AudioFormat* const* begin() const noexcept { return knownFormats.begin(); } + + /** Iterator access to the list of known formats. */ + AudioFormat** end() noexcept { return knownFormats.end(); } + + /** Iterator access to the list of known formats. */ + AudioFormat* const* end() const noexcept { return knownFormats.end(); } /** Looks for which of the known formats is listed as being for a given file extension. diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp index c68c6d8..65ef3fb 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp @@ -75,7 +75,7 @@ bool AudioFormatReader::read (int* const* destChannels, for (int i = numDestChannels; --i >= 0;) if (auto d = destChannels[i]) - zeromem (d, sizeof (int) * (size_t) silence); + zeromem (d, (size_t) silence * sizeof (int)); startOffsetInDestBuffer += silence; numSamplesToRead -= silence; @@ -175,7 +175,7 @@ void AudioFormatReader::read (AudioBuffer* buffer, // if the target's stereo and the source is mono, dupe the first channel.. if (numTargetChannels > 1 && (chans[0] == nullptr || chans[1] == nullptr)) - memcpy (dests[1], dests[0], sizeof (float) * (size_t) numSamples); + memcpy (dests[1], dests[0], (size_t) numSamples * sizeof (float)); if (! usesFloatingPointData) convertFixedToFloat (dests, 2, numSamples); diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.h b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.h index 4a472a1..9bc74a9 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.h +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.h @@ -314,7 +314,7 @@ protected: { for (int i = numDestChannels; --i >= 0;) if (destChannels[i] != nullptr) - zeromem (destChannels[i] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples); + zeromem (destChannels[i] + startOffsetInDestBuffer, (size_t) numSamples * sizeof (int)); numSamples = (int) samplesAvailable; } diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h index 5e19731..e3240ca 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h @@ -73,6 +73,7 @@ public: void readMaxLevels (int64 startSample, int64 numSamples, Range* results, int numChannelsToRead) override; + using AudioFormatReader::readMaxLevels; private: //============================================================================== diff --git a/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.h b/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.h index aac14d9..1247742 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.h +++ b/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.h @@ -24,6 +24,7 @@ ============================================================================== */ + /******************************************************************************* The block below describes the properties of this module, and is read by the Projucer to automatically generate project code that uses it. @@ -33,17 +34,17 @@ BEGIN_JUCE_MODULE_DECLARATION - ID: juce_audio_formats - vendor: juce - version: 5.4.3 - name: JUCE audio file format codecs - description: Classes for reading and writing various audio file formats. - website: http://www.juce.com/juce - license: GPL/Commercial + ID: juce_audio_formats + vendor: juce + version: 5.4.7 + name: JUCE audio file format codecs + description: Classes for reading and writing various audio file formats. + website: http://www.juce.com/juce + license: GPL/Commercial - dependencies: juce_audio_basics - OSXFrameworks: CoreAudio CoreMIDI QuartzCore AudioToolbox - iOSFrameworks: AudioToolbox QuartzCore + dependencies: juce_audio_basics + OSXFrameworks: CoreAudio CoreMIDI QuartzCore AudioToolbox + iOSFrameworks: AudioToolbox QuartzCore END_JUCE_MODULE_DECLARATION diff --git a/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.h b/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.h index b29ad42..26b2668 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.h +++ b/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.h @@ -138,6 +138,7 @@ public: void controllerMoved (int controllerNumber, int newValue) override; void renderNextBlock (AudioBuffer&, int startSample, int numSamples) override; + using SynthesiserVoice::renderNextBlock; private: //============================================================================== diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.cpp index fce82ba..7917a22 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.cpp @@ -166,13 +166,16 @@ AbstractFifo::ScopedRead AbstractFifo::read (int numToRead) noexcept { ret AbstractFifo::ScopedWrite AbstractFifo::write (int numToWrite) noexcept { return { *this, numToWrite }; } +//============================================================================== //============================================================================== #if JUCE_UNIT_TESTS class AbstractFifoTests : public UnitTest { public: - AbstractFifoTests() : UnitTest ("Abstract Fifo", "Containers") {} + AbstractFifoTests() + : UnitTest ("Abstract Fifo", UnitTestCategories::containers) + {} struct WriteThread : public Thread { diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_Array.h b/JuceLibraryCode/modules/juce_core/containers/juce_Array.h index 6a8c736..a2a3718 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_Array.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_Array.h @@ -30,7 +30,7 @@ namespace juce Examples of arrays are: Array, Array or Array The Array class can be used to hold simple, non-polymorphic objects as well as primitive types - to - do so, the class must fulfil these requirements: + do so, the class must fulfill these requirements: - it must have a copy constructor and assignment operator - it must be able to be relocated in memory by a memcpy without this causing any problems - so objects whose functionality relies on external pointers or references to themselves can not be used. @@ -264,7 +264,21 @@ public: @param index the index of the element being requested (0 is the first element in the array) @see operator[], getFirst, getLast */ - inline ElementType& getReference (int index) const noexcept + inline ElementType& getReference (int index) noexcept + { + const ScopedLockType lock (getLock()); + return values[index]; + } + + /** Returns a direct reference to one of the elements in the array, without checking the index passed in. + + This is like getUnchecked, but returns a direct reference to the element. Obviously + this can be dangerous, so only use it when absolutely necessary. + + @param index the index of the element being requested (0 is the first element in the array) + @see operator[], getFirst, getLast + */ + inline const ElementType& getReference (int index) const noexcept { const ScopedLockType lock (getLock()); return values[index]; @@ -298,11 +312,28 @@ public: return values.begin(); } + /** Returns a pointer to the actual array data. + This pointer will only be valid until the next time a non-const method + is called on the array. + */ + inline const ElementType* getRawDataPointer() const noexcept + { + return values.begin(); + } + //============================================================================== /** Returns a pointer to the first element in the array. This method is provided for compatibility with standard C++ iteration mechanisms. */ - inline ElementType* begin() const noexcept + inline ElementType* begin() noexcept + { + return values.begin(); + } + + /** Returns a pointer to the first element in the array. + This method is provided for compatibility with standard C++ iteration mechanisms. + */ + inline const ElementType* begin() const noexcept { return values.begin(); } @@ -310,7 +341,15 @@ public: /** Returns a pointer to the element which follows the last element in the array. This method is provided for compatibility with standard C++ iteration mechanisms. */ - inline ElementType* end() const noexcept + inline ElementType* end() noexcept + { + return values.end(); + } + + /** Returns a pointer to the element which follows the last element in the array. + This method is provided for compatibility with standard C++ iteration mechanisms. + */ + inline const ElementType* end() const noexcept { return values.end(); } @@ -318,7 +357,15 @@ public: /** Returns a pointer to the first element in the array. This method is provided for compatibility with the standard C++ containers. */ - inline ElementType* data() const noexcept + inline ElementType* data() noexcept + { + return begin(); + } + + /** Returns a pointer to the first element in the array. + This method is provided for compatibility with the standard C++ containers. + */ + inline const ElementType* data() const noexcept { return begin(); } diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_ArrayBase.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_ArrayBase.cpp index e0e3513..7a79da6 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_ArrayBase.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_ArrayBase.cpp @@ -113,7 +113,7 @@ class ArrayBaseTests : public UnitTest public: ArrayBaseTests() - : UnitTest ("ArrayBase", "Containers") + : UnitTest ("ArrayBase", UnitTestCategories::containers) {} void runTest() override @@ -304,7 +304,7 @@ public: checkEqual (copyableContainer, noncopyableContainer, referenceContainer); } - beginTest ("add array from initilizer list"); + beginTest ("add array from initializer_list"); { std::vector referenceContainer; ArrayBase copyableContainer; diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_ArrayBase.h b/JuceLibraryCode/modules/juce_core/containers/juce_ArrayBase.h index 2294c54..b4e7f38 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_ArrayBase.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_ArrayBase.h @@ -136,7 +136,14 @@ public: } //============================================================================== - inline ElementType& operator[] (const int index) const noexcept + inline ElementType& operator[] (const int index) noexcept + { + jassert (elements != nullptr); + jassert (isPositiveAndBelow (index, numUsed)); + return elements[index]; + } + + inline const ElementType& operator[] (const int index) const noexcept { jassert (elements != nullptr); jassert (isPositiveAndBelow (index, numUsed)); @@ -159,17 +166,32 @@ public: } //============================================================================== - inline ElementType* begin() const noexcept + inline ElementType* begin() noexcept { return elements; } - inline ElementType* end() const noexcept + inline const ElementType* begin() const noexcept + { + return elements; + } + + inline ElementType* end() noexcept { return elements + numUsed; } - inline ElementType* data() const noexcept + inline const ElementType* end() const noexcept + { + return elements + numUsed; + } + + inline ElementType* data() noexcept + { + return elements; + } + + inline const ElementType* data() const noexcept { return elements; } @@ -499,13 +521,13 @@ private: { memmove (elements + currentIndex, elements + currentIndex + 1, - sizeof (ElementType) * (size_t) (newIndex - currentIndex)); + (size_t) (newIndex - currentIndex) * sizeof (ElementType)); } else { memmove (elements + newIndex + 1, elements + newIndex, - sizeof (ElementType) * (size_t) (currentIndex - newIndex)); + (size_t) (currentIndex - newIndex) * sizeof (ElementType)); } memcpy (elements + newIndex, tempCopy, sizeof (ElementType)); diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.h b/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.h index 9085e09..aed2108 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.h @@ -78,7 +78,7 @@ public: call, then it invokes it. This method is virtual to allow more dynamic invocation to used for objects - where the methods may not already be set as properies. + where the methods may not already be set as properties. */ virtual var invokeMethod (Identifier methodName, const var::NativeFunctionArgs& args); diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_HashMap.h b/JuceLibraryCode/modules/juce_core/containers/juce_HashMap.h index 5dc3050..a5ab9ea 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_HashMap.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_HashMap.h @@ -203,7 +203,7 @@ public: } //============================================================================== - /** Returns true if the map contains an item with the specied key. */ + /** Returns true if the map contains an item with the specified key. */ bool contains (KeyTypeParameter keyToLookFor) const { const ScopedLockType sl (getLock()); diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_HashMap_test.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_HashMap_test.cpp index 76dcd87..fe58175 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_HashMap_test.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_HashMap_test.cpp @@ -25,7 +25,9 @@ namespace juce struct HashMapTest : public UnitTest { - HashMapTest() : UnitTest ("HashMap", "Containers") {} + HashMapTest() + : UnitTest ("HashMap", UnitTestCategories::containers) + {} void runTest() override { diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.cpp index 339e60e..b6b9828 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.cpp @@ -147,7 +147,16 @@ var NamedValueSet::getWithDefault (const Identifier& name, const var& defaultRet return defaultReturnValue; } -var* NamedValueSet::getVarPointer (const Identifier& name) const noexcept +var* NamedValueSet::getVarPointer (const Identifier& name) noexcept +{ + for (auto& i : values) + if (i.name == name) + return &(i.value); + + return {}; +} + +const var* NamedValueSet::getVarPointer (const Identifier& name) const noexcept { for (auto& i : values) if (i.name == name) @@ -236,7 +245,15 @@ const var& NamedValueSet::getValueAt (const int index) const noexcept return getNullVarRef(); } -var* NamedValueSet::getVarPointerAt (int index) const noexcept +var* NamedValueSet::getVarPointerAt (int index) noexcept +{ + if (isPositiveAndBelow (index, values.size())) + return &(values.getReference (index).value); + + return {}; +} + +const var* NamedValueSet::getVarPointerAt (int index) const noexcept { if (isPositiveAndBelow (index, values.size())) return &(values.getReference (index).value); diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.h b/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.h index d76651d..9e8f153 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.h @@ -89,7 +89,6 @@ public: /** Returns the value of a named item. If the name isn't found, this will return a void variant. - @see getProperty */ const var& operator[] (const Identifier& name) const noexcept; @@ -132,7 +131,17 @@ public: Also note that the pointer returned may become invalid as soon as any subsequent methods are called on the NamedValueSet. */ - var* getVarPointer (const Identifier& name) const noexcept; + var* getVarPointer (const Identifier& name) noexcept; + + /** Returns a pointer to the var that holds a named value, or null if there is + no value with this name. + + Do not use this method unless you really need access to the internal var object + for some reason - for normal reading and writing always prefer operator[]() and set(). + Also note that the pointer returned may become invalid as soon as any subsequent + methods are called on the NamedValueSet. + */ + const var* getVarPointer (const Identifier& name) const noexcept; /** Returns the value of the item at a given index. The index must be between 0 and size() - 1. @@ -144,7 +153,14 @@ public: Also note that the pointer returned may become invalid as soon as any subsequent methods are called on the NamedValueSet. */ - var* getVarPointerAt (int index) const noexcept; + var* getVarPointerAt (int index) noexcept; + + /** Returns the value of the item at a given index. + The index must be between 0 and size() - 1, or this will return a nullptr + Also note that the pointer returned may become invalid as soon as any subsequent + methods are called on the NamedValueSet. + */ + const var* getVarPointerAt (int index) const noexcept; /** Returns the index of the given name, or -1 if it's not found. */ int indexOf (const Identifier& name) const noexcept; diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.cpp index acb4027..480c5ec 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.cpp @@ -27,17 +27,51 @@ namespace juce static struct OwnedArrayTest : public UnitTest { - OwnedArrayTest() : UnitTest { "OwnedArray" } {} - struct Base { + Base() = default; virtual ~Base() = default; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Base) }; struct Derived : Base { + Derived() = default; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Derived) }; + struct DestructorObj + { + DestructorObj (OwnedArrayTest& p, + OwnedArray& arr) + : parent (p), objectArray (arr) + {} + + ~DestructorObj() + { + data = 0; + + for (auto* o : objectArray) + { + parent.expect (o != nullptr); + parent.expect (o != this); + parent.expectEquals (o->data, 956); + } + } + + OwnedArrayTest& parent; + OwnedArray& objectArray; + int data = 956; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DestructorObj) + }; + + OwnedArrayTest() + : UnitTest ("OwnedArray", UnitTestCategories::containers) + {} + void runTest() override { beginTest ("After converting move construction, ownership is transferred"); @@ -58,6 +92,34 @@ static struct OwnedArrayTest : public UnitTest expectEquals (base.size(), 3); } + + beginTest ("Iterate in destructor"); + { + { + OwnedArray arr; + + for (int i = 0; i < 2; ++i) + arr.add (new DestructorObj (*this, arr)); + } + + OwnedArray arr; + + for (int i = 0; i < 1025; ++i) + arr.add (new DestructorObj (*this, arr)); + + while (! arr.isEmpty()) + arr.remove (0); + + for (int i = 0; i < 1025; ++i) + arr.add (new DestructorObj (*this, arr)); + + arr.removeRange (1, arr.size() - 3); + + for (int i = 0; i < 1025; ++i) + arr.add (new DestructorObj (*this, arr)); + + arr.set (500, new DestructorObj (*this, arr)); + } } } ownedArrayTest; diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.h b/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.h index 8393b70..f3e9266 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.h @@ -146,7 +146,7 @@ public: @see getUnchecked */ - inline ObjectClass* operator[] (const int index) const noexcept + inline ObjectClass* operator[] (int index) const noexcept { const ScopedLockType lock (getLock()); return values.getValueWithDefault (index); @@ -157,7 +157,7 @@ public: This is a faster and less safe version of operator[] which doesn't check the index passed in, so it can be used when you're sure the index is always going to be legal. */ - inline ObjectClass* getUnchecked (const int index) const noexcept + inline ObjectClass* getUnchecked (int index) const noexcept { const ScopedLockType lock (getLock()); return values[index]; @@ -198,7 +198,15 @@ public: /** Returns a pointer to the first element in the array. This method is provided for compatibility with standard C++ iteration mechanisms. */ - inline ObjectClass** begin() const noexcept + inline ObjectClass** begin() noexcept + { + return values.begin(); + } + + /** Returns a pointer to the first element in the array. + This method is provided for compatibility with standard C++ iteration mechanisms. + */ + inline ObjectClass* const* begin() const noexcept { return values.begin(); } @@ -206,7 +214,15 @@ public: /** Returns a pointer to the element which follows the last element in the array. This method is provided for compatibility with standard C++ iteration mechanisms. */ - inline ObjectClass** end() const noexcept + inline ObjectClass** end() noexcept + { + return values.end(); + } + + /** Returns a pointer to the element which follows the last element in the array. + This method is provided for compatibility with standard C++ iteration mechanisms. + */ + inline ObjectClass* const* end() const noexcept { return values.end(); } @@ -214,7 +230,15 @@ public: /** Returns a pointer to the first element in the array. This method is provided for compatibility with the standard C++ containers. */ - inline ObjectClass** data() const noexcept + inline ObjectClass** data() noexcept + { + return begin(); + } + + /** Returns a pointer to the first element in the array. + This method is provided for compatibility with the standard C++ containers. + */ + inline ObjectClass* const* data() const noexcept { return begin(); } @@ -228,7 +252,7 @@ public: int indexOf (const ObjectClass* objectToLookFor) const noexcept { const ScopedLockType lock (getLock()); - auto** e = values.begin(); + auto* e = values.begin(); for (; e != values.end(); ++e) if (objectToLookFor == *e) @@ -245,7 +269,7 @@ public: bool contains (const ObjectClass* objectToLookFor) const noexcept { const ScopedLockType lock (getLock()); - auto** e = values.begin(); + auto* e = values.begin(); for (; e != values.end(); ++e) if (objectToLookFor == *e) @@ -257,27 +281,44 @@ public: //============================================================================== /** Appends a new object to the end of the array. - Note that the this object will be deleted by the OwnedArray when it - is removed, so be careful not to delete it somewhere else. + Note that this object will be deleted by the OwnedArray when it is removed, + so be careful not to delete it somewhere else. Also be careful not to add the same object to the array more than once, as this will obviously cause deletion of dangling pointers. @param newObject the new object to add to the array @returns the new object that was added - @see set, insert, addIfNotAlreadyThere, addSorted + @see set, insert, addSorted */ - ObjectClass* add (ObjectClass* newObject) noexcept + ObjectClass* add (ObjectClass* newObject) { const ScopedLockType lock (getLock()); values.add (newObject); return newObject; } + /** Appends a new object to the end of the array. + + Note that this object will be deleted by the OwnedArray when it is removed, + so be careful not to delete it somewhere else. + + Also be careful not to add the same object to the array more than once, + as this will obviously cause deletion of dangling pointers. + + @param newObject the new object to add to the array + @returns the new object that was added + @see set, insert, addSorted + */ + ObjectClass* add (std::unique_ptr newObject) + { + return add (newObject.release()); + } + /** Inserts a new object into the array at the given index. - Note that the this object will be deleted by the OwnedArray when it - is removed, so be careful not to delete it somewhere else. + Note that this object will be deleted by the OwnedArray when it is removed, + so be careful not to delete it somewhere else. If the index is less than 0 or greater than the size of the array, the element will be added to the end of the array. @@ -290,15 +331,38 @@ public: @param indexToInsertAt the index at which the new element should be inserted @param newObject the new object to add to the array @returns the new object that was added - @see add, addSorted, addIfNotAlreadyThere, set + @see add, addSorted, set */ - ObjectClass* insert (int indexToInsertAt, ObjectClass* newObject) noexcept + ObjectClass* insert (int indexToInsertAt, ObjectClass* newObject) { const ScopedLockType lock (getLock()); values.insert (indexToInsertAt, newObject, 1); return newObject; } + /** Inserts a new object into the array at the given index. + + Note that this object will be deleted by the OwnedArray when it is removed, + so be careful not to delete it somewhere else. + + If the index is less than 0 or greater than the size of the array, the + element will be added to the end of the array. + Otherwise, it will be inserted into the array, moving all the later elements + along to make room. + + Be careful not to add the same object to the array more than once, + as this will obviously cause deletion of dangling pointers. + + @param indexToInsertAt the index at which the new element should be inserted + @param newObject the new object to add to the array + @returns the new object that was added + @see add, addSorted, set + */ + ObjectClass* insert (int indexToInsertAt, std::unique_ptr newObject) + { + return insert (indexToInsertAt, newObject.release()); + } + /** Inserts an array of values into this array at a given position. If the index is less than 0 or greater than the size of the array, the @@ -322,25 +386,6 @@ public: } } - /** Appends a new object at the end of the array as long as the array doesn't - already contain it. - - If the array already contains a matching object, nothing will be done. - - @param newObject the new object to add to the array - @returns true if the new object was added, false otherwise - */ - bool addIfNotAlreadyThere (ObjectClass* newObject) noexcept - { - const ScopedLockType lock (getLock()); - - if (contains (newObject)) - return false; - - add (newObject); - return true; - } - /** Replaces an object in the array with a different one. If the index is less than zero, this method does nothing. @@ -390,6 +435,24 @@ public: return newObject; } + /** Replaces an object in the array with a different one. + + If the index is less than zero, this method does nothing. + If the index is beyond the end of the array, the new object is added to the end of the array. + + Be careful not to add the same object to the array more than once, + as this will obviously cause deletion of dangling pointers. + + @param indexToChange the index whose value you want to change + @param newObject the new value to set for this index. + @param deleteOldElement whether to delete the object that's being replaced with the new one + @see add, insert, remove + */ + ObjectClass* set (int indexToChange, std::unique_ptr newObject, bool deleteOldElement = true) + { + return set (indexToChange, newObject.release(), deleteOldElement); + } + /** Adds elements from another array to the end of this array. @param arrayToAddFrom the array from which to copy the elements @@ -468,14 +531,14 @@ public: @see add, sort, indexOfSorted */ template - int addSorted (ElementComparator& comparator, ObjectClass* const newObject) noexcept + int addSorted (ElementComparator& comparator, ObjectClass* newObject) noexcept { // If you pass in an object with a static compareElements() method, this // avoids getting warning messages about the parameter being unused ignoreUnused (comparator); const ScopedLockType lock (getLock()); - const int index = findInsertIndexInSortedArray (comparator, values.begin(), newObject, 0, values.size()); + auto index = findInsertIndexInSortedArray (comparator, values.begin(), newObject, 0, values.size()); insert (index, newObject); return index; } @@ -493,7 +556,7 @@ public: @see addSorted, sort */ template - int indexOfSorted (ElementComparator& comparator, const ObjectClass* const objectToLookFor) const noexcept + int indexOfSorted (ElementComparator& comparator, const ObjectClass* objectToLookFor) const noexcept { // If you pass in an object with a static compareElements() method, this // avoids getting warning messages about the parameter being unused @@ -625,17 +688,16 @@ public: if (numberToRemove > 0) { + Array objectsToDelete; + if (deleteObjects) - { - for (int i = startIndex; i < endIndex; ++i) - { - ContainerDeletePolicy::destroy (values[i]); - values[i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer) - } - } + objectsToDelete.addArray (values.begin() + startIndex, numberToRemove); values.removeElements (startIndex, numberToRemove); + for (auto& o : objectsToDelete) + ContainerDeletePolicy::destroy (o); + if ((values.size() << 1) < values.capacity()) minimiseStorageOverheads(); } @@ -723,7 +785,7 @@ public: the array won't have to keep dynamically resizing itself as the elements are added, and it'll therefore be more efficient. */ - void ensureStorageAllocated (const int minNumElements) noexcept + void ensureStorageAllocated (int minNumElements) noexcept { const ScopedLockType lock (getLock()); values.ensureAllocatedSize (minNumElements); @@ -757,7 +819,7 @@ public: */ template void sort (ElementComparator& comparator, - bool retainOrderOfEquivalentItems = false) const noexcept + bool retainOrderOfEquivalentItems = false) noexcept { // If you pass in an object with a static compareElements() method, this // avoids getting warning messages about the parameter being unused @@ -792,10 +854,14 @@ private: void deleteAllObjects() { - for (auto& e : values) - ContainerDeletePolicy::destroy (e); + auto i = values.size(); - values.clear(); + while (--i >= 0) + { + auto* e = values[i]; + values.removeElements (i, 1); + ContainerDeletePolicy::destroy (e); + } } template diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.cpp index b1938de..fe37127 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.cpp @@ -23,7 +23,7 @@ namespace juce { -PropertySet::PropertySet (const bool ignoreCaseOfKeyNames) +PropertySet::PropertySet (bool ignoreCaseOfKeyNames) : properties (ignoreCaseOfKeyNames), fallbackProperties (nullptr), ignoreCaseOfKeys (ignoreCaseOfKeyNames) @@ -65,8 +65,7 @@ void PropertySet::clear() String PropertySet::getValue (StringRef keyName, const String& defaultValue) const noexcept { const ScopedLock sl (lock); - - const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); + auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); if (index >= 0) return properties.getAllValues() [index]; @@ -75,10 +74,10 @@ String PropertySet::getValue (StringRef keyName, const String& defaultValue) con : defaultValue; } -int PropertySet::getIntValue (StringRef keyName, const int defaultValue) const noexcept +int PropertySet::getIntValue (StringRef keyName, int defaultValue) const noexcept { const ScopedLock sl (lock); - const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); + auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); if (index >= 0) return properties.getAllValues() [index].getIntValue(); @@ -87,10 +86,10 @@ int PropertySet::getIntValue (StringRef keyName, const int defaultValue) const n : defaultValue; } -double PropertySet::getDoubleValue (StringRef keyName, const double defaultValue) const noexcept +double PropertySet::getDoubleValue (StringRef keyName, double defaultValue) const noexcept { const ScopedLock sl (lock); - const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); + auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); if (index >= 0) return properties.getAllValues()[index].getDoubleValue(); @@ -99,10 +98,10 @@ double PropertySet::getDoubleValue (StringRef keyName, const double defaultValue : defaultValue; } -bool PropertySet::getBoolValue (StringRef keyName, const bool defaultValue) const noexcept +bool PropertySet::getBoolValue (StringRef keyName, bool defaultValue) const noexcept { const ScopedLock sl (lock); - const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); + auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); if (index >= 0) return properties.getAllValues() [index].getIntValue() != 0; @@ -111,9 +110,9 @@ bool PropertySet::getBoolValue (StringRef keyName, const bool defaultValue) cons : defaultValue; } -XmlElement* PropertySet::getXmlValue (StringRef keyName) const +std::unique_ptr PropertySet::getXmlValue (StringRef keyName) const { - return XmlDocument::parse (getValue (keyName)); + return parseXML (getValue (keyName)); } void PropertySet::setValue (const String& keyName, const var& v) @@ -122,10 +121,9 @@ void PropertySet::setValue (const String& keyName, const var& v) if (keyName.isNotEmpty()) { - const String value (v.toString()); + auto value = v.toString(); const ScopedLock sl (lock); - - const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); + auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); if (index < 0 || properties.getAllValues() [index] != value) { @@ -140,7 +138,7 @@ void PropertySet::removeValue (StringRef keyName) if (keyName.isNotEmpty()) { const ScopedLock sl (lock); - const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); + auto index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); if (index >= 0) { @@ -150,10 +148,10 @@ void PropertySet::removeValue (StringRef keyName) } } -void PropertySet::setValue (const String& keyName, const XmlElement* const xml) +void PropertySet::setValue (const String& keyName, const XmlElement* xml) { setValue (keyName, xml == nullptr ? var() - : var (xml->createDocument ("", true))); + : var (xml->toString (XmlElement::TextFormat().singleLine().withoutHeader()))); } bool PropertySet::containsKey (StringRef keyName) const noexcept @@ -177,14 +175,15 @@ void PropertySet::setFallbackPropertySet (PropertySet* fallbackProperties_) noex fallbackProperties = fallbackProperties_; } -XmlElement* PropertySet::createXml (const String& nodeName) const +std::unique_ptr PropertySet::createXml (const String& nodeName) const { + auto xml = std::make_unique (nodeName); + const ScopedLock sl (lock); - XmlElement* const xml = new XmlElement (nodeName); for (int i = 0; i < properties.getAllKeys().size(); ++i) { - XmlElement* const e = xml->createNewChildElement ("VALUE"); + auto e = xml->createNewChildElement ("VALUE"); e->setAttribute ("name", properties.getAllKeys()[i]); e->setAttribute ("val", properties.getAllValues()[i]); } diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.h b/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.h index de4f424..b14cabf 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.h @@ -113,7 +113,7 @@ public: @param keyName the name of the property to retrieve */ - XmlElement* getXmlValue (StringRef keyName) const; + std::unique_ptr getXmlValue (StringRef keyName) const; //============================================================================== /** Sets a named property. @@ -143,7 +143,7 @@ public: */ void removeValue (StringRef keyName); - /** Returns true if the properies include the given key. */ + /** Returns true if the properties include the given key. */ bool containsKey (StringRef keyName) const noexcept; /** Removes all values. */ @@ -161,7 +161,7 @@ public: The string parameter is the tag name that should be used for the node. @see restoreFromXml */ - XmlElement* createXml (const String& nodeName) const; + std::unique_ptr createXml (const String& nodeName) const; /** Reloads a set of properties that were previously stored as XML. The node passed in must have been created by the createXml() method. @@ -190,7 +190,7 @@ public: PropertySet* getFallbackPropertySet() const noexcept { return fallbackProperties; } protected: - /** Subclasses can override this to be told when one of the properies has been changed. */ + /** Subclasses can override this to be told when one of the properties has been changed. */ virtual void propertyChanged(); private: diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.cpp index 15d380f..75d6c8d 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.cpp @@ -28,7 +28,9 @@ namespace juce class ReferenceCountedArrayTests : public UnitTest { public: - ReferenceCountedArrayTests() : UnitTest ("ReferenceCountedArray", "Containers") {} + ReferenceCountedArrayTests() + : UnitTest ("ReferenceCountedArray", UnitTestCategories::containers) + {} //============================================================================== void runTest() override @@ -82,12 +84,7 @@ public: expectEquals (derivedObject->getReferenceCount(), 1); baseArray.add (baseObjectPtr); - - #if JUCE_STRICT_REFCOUNTEDPOINTER - baseArray.add (derivedObjectPtr); - #else baseArray.add (derivedObjectPtr.get()); - #endif for (auto o : baseArray) expectEquals (o->getReferenceCount(), 2); @@ -97,6 +94,34 @@ public: for (auto o : derivedArray) expectEquals (o->getReferenceCount(), 3); } + + beginTest ("Iterate in destructor"); + { + { + ReferenceCountedArray arr; + + for (int i = 0; i < 2; ++i) + arr.add (new DestructorObj (*this, arr)); + } + + ReferenceCountedArray arr; + + for (int i = 0; i < 1025; ++i) + arr.add (new DestructorObj (*this, arr)); + + while (! arr.isEmpty()) + arr.remove (0); + + for (int i = 0; i < 1025; ++i) + arr.add (new DestructorObj (*this, arr)); + + arr.removeRange (1, arr.size() - 3); + + for (int i = 0; i < 1025; ++i) + arr.add (new DestructorObj (*this, arr)); + + arr.set (500, new DestructorObj (*this, arr)); + } } private: @@ -105,6 +130,8 @@ private: using Ptr = ReferenceCountedObjectPtr; TestBaseObj() = default; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestBaseObj) }; struct TestDerivedObj : public TestBaseObj @@ -112,6 +139,34 @@ private: using Ptr = ReferenceCountedObjectPtr; TestDerivedObj() = default; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestDerivedObj) + }; + + struct DestructorObj : public ReferenceCountedObject + { + DestructorObj (ReferenceCountedArrayTests& p, + ReferenceCountedArray& arr) + : parent (p), objectArray (arr) + {} + + ~DestructorObj() + { + data = 0; + + for (auto* o : objectArray) + { + parent.expect (o != nullptr); + parent.expect (o != this); + parent.expectEquals (o->data, 374); + } + } + + ReferenceCountedArrayTests& parent; + ReferenceCountedArray& objectArray; + int data = 374; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DestructorObj) }; }; diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.h b/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.h index db9eded..221c09e 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.h @@ -240,7 +240,15 @@ public: /** Returns a pointer to the first element in the array. This method is provided for compatibility with standard C++ iteration mechanisms. */ - inline ObjectClass** begin() const noexcept + inline ObjectClass** begin() noexcept + { + return values.begin(); + } + + /** Returns a pointer to the first element in the array. + This method is provided for compatibility with standard C++ iteration mechanisms. + */ + inline ObjectClass* const* begin() const noexcept { return values.begin(); } @@ -248,7 +256,15 @@ public: /** Returns a pointer to the element which follows the last element in the array. This method is provided for compatibility with standard C++ iteration mechanisms. */ - inline ObjectClass** end() const noexcept + inline ObjectClass** end() noexcept + { + return values.end(); + } + + /** Returns a pointer to the element which follows the last element in the array. + This method is provided for compatibility with standard C++ iteration mechanisms. + */ + inline ObjectClass* const* end() const noexcept { return values.end(); } @@ -256,7 +272,15 @@ public: /** Returns a pointer to the first element in the array. This method is provided for compatibility with the standard C++ containers. */ - inline ObjectClass** data() const noexcept + inline ObjectClass** data() noexcept + { + return begin(); + } + + /** Returns a pointer to the first element in the array. + This method is provided for compatibility with the standard C++ containers. + */ + inline ObjectClass* const* data() const noexcept { return begin(); } @@ -270,8 +294,8 @@ public: int indexOf (const ObjectClass* objectToLookFor) const noexcept { const ScopedLockType lock (getLock()); - auto** e = values.begin(); - auto** endPointer = values.end(); + auto* e = values.begin(); + auto* endPointer = values.end(); while (e != endPointer) { @@ -299,8 +323,8 @@ public: bool contains (const ObjectClass* objectToLookFor) const noexcept { const ScopedLockType lock (getLock()); - auto** e = values.begin(); - auto** endPointer = values.end(); + auto* e = values.begin(); + auto* endPointer = values.end(); while (e != endPointer) { @@ -437,8 +461,9 @@ public: if (indexToChange < values.size()) { - releaseObject (values[indexToChange]); + auto* e = values[indexToChange]; values[indexToChange] = newObject; + releaseObject (e); } else { @@ -447,6 +472,20 @@ public: } } + /** Replaces an object in the array with a different one. + + If the index is less than zero, this method does nothing. + If the index is beyond the end of the array, the new object is added to the end of the array. + + The object being added has its reference count increased, and if it's replacing + another object, then that one has its reference count decreased, and may be deleted. + + @param indexToChange the index whose value you want to change + @param newObject the new value to set for this index. + @see add, insert, remove + */ + void set (int indexToChange, const ObjectClassPtr& newObject) { set (indexToChange, newObject.get()); } + /** Adds elements from another array to the end of this array. @param arrayToAddFrom the array from which to copy the elements @@ -570,9 +609,9 @@ public: if (isPositiveAndBelow (indexToRemove, values.size())) { - auto** e = values.begin() + indexToRemove; - releaseObject (*e); + auto* e = *(values.begin() + indexToRemove); values.removeElements (indexToRemove, 1); + releaseObject (e); if ((values.size() << 1) < values.capacity()) minimiseStorageOverheads(); @@ -595,10 +634,10 @@ public: if (isPositiveAndBelow (indexToRemove, values.size())) { - auto** e = values.begin() + indexToRemove; - removedItem = *e; - releaseObject (*e); + auto* e = *(values.begin() + indexToRemove); + removedItem = e; values.removeElements (indexToRemove, 1); + releaseObject (e); if ((values.size() << 1) < values.capacity()) minimiseStorageOverheads(); @@ -656,14 +695,14 @@ public: if (numberToRemove > 0) { - for (int i = startIndex; i < endIndex; ++i) - { - releaseObject (values[i]); - values[i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer) - } + Array objectsToRemove; + objectsToRemove.addArray (values.begin() + startIndex, numberToRemove); values.removeElements (startIndex, numberToRemove); + for (auto& o : objectsToRemove) + releaseObject (o); + if ((values.size() << 1) < values.capacity()) minimiseStorageOverheads(); } @@ -790,7 +829,7 @@ public: */ template void sort (ElementComparator& comparator, - bool retainOrderOfEquivalentItems = false) const noexcept + bool retainOrderOfEquivalentItems = false) noexcept { // If you pass in an object with a static compareElements() method, this // avoids getting warning messages about the parameter being unused @@ -848,10 +887,14 @@ private: void releaseAllObjects() { - for (auto& v : values) - releaseObject (v); + auto i = values.size(); - values.clear(); + while (--i >= 0) + { + auto* e = values[i]; + values.removeElements (i, 1); + releaseObject (e); + } } static void releaseObject (ObjectClass* o) diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_SortedSet.h b/JuceLibraryCode/modules/juce_core/containers/juce_SortedSet.h index ef7e3a9..b3f7905 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_SortedSet.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_SortedSet.h @@ -58,22 +58,19 @@ class SortedSet public: //============================================================================== /** Creates an empty set. */ - // VS2013 doesn't allow defaulted noexcept constructors. SortedSet() = default; /** Creates a copy of another set. */ SortedSet (const SortedSet&) = default; /** Creates a copy of another set. */ - // VS2013 doesn't allow defaulted noexcept constructors. - SortedSet (SortedSet&& other) noexcept : data (std::move (other.data)) {} + SortedSet (SortedSet&&) noexcept = default; /** Makes a copy of another set. */ SortedSet& operator= (const SortedSet&) = default; /** Makes a copy of another set. */ - // VS2013 doesn't allow defaulted noexcept constructors. - SortedSet& operator= (SortedSet&& other) noexcept { data = std::move (other.data); return *this; } + SortedSet& operator= (SortedSet&&) noexcept = default; /** Destructor. */ ~SortedSet() = default; @@ -169,7 +166,16 @@ public: @param index the index of the element being requested (0 is the first element in the array) */ - inline ElementType& getReference (const int index) const noexcept + inline ElementType& getReference (const int index) noexcept + { + return data.getReference (index); + } + + /** Returns a direct reference to one of the elements in the set, without checking the index passed in. + + @param index the index of the element being requested (0 is the first element in the array) + */ + inline const ElementType& getReference (const int index) const noexcept { return data.getReference (index); } @@ -194,7 +200,7 @@ public: /** Returns a pointer to the first element in the set. This method is provided for compatibility with standard C++ iteration mechanisms. */ - inline ElementType* begin() const noexcept + inline const ElementType* begin() const noexcept { return data.begin(); } @@ -202,7 +208,7 @@ public: /** Returns a pointer to the element which follows the last element in the set. This method is provided for compatibility with standard C++ iteration mechanisms. */ - inline ElementType* end() const noexcept + inline const ElementType* end() const noexcept { return data.end(); } diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_SparseSet.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_SparseSet.cpp index da215de..cffb7b9 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_SparseSet.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_SparseSet.cpp @@ -28,7 +28,9 @@ namespace juce class SparseSetTests : public UnitTest { public: - SparseSetTests() : UnitTest ("SparseSet class", "Containers") {} + SparseSetTests() + : UnitTest ("SparseSet class", UnitTestCategories::containers) + {} void runTest() override { diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_Variant.h b/JuceLibraryCode/modules/juce_core/containers/juce_Variant.h index a25b9e2..2596576 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_Variant.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_Variant.h @@ -49,15 +49,12 @@ public: { NativeFunctionArgs (const var& thisObject, const var* args, int numArgs) noexcept; - // Suppress a VS2013 compiler warning - NativeFunctionArgs& operator= (const NativeFunctionArgs&) = delete; - const var& thisObject; const var* arguments; int numArguments; }; - using NativeFunction = std::function; + using NativeFunction = std::function; //============================================================================== /** Creates a void variant. */ @@ -305,7 +302,7 @@ private: int64 int64Value; bool boolValue; double doubleValue; - char stringValue [sizeof (String)]; + char stringValue[sizeof (String)]; ReferenceCountedObject* objectValue; MemoryBlock* binaryValue; NativeFunction* methodValue; @@ -322,6 +319,8 @@ private: // This is needed to prevent the wrong constructor/operator being called var (const ReferenceCountedObject*) = delete; var& operator= (const ReferenceCountedObject*) = delete; + var (const void*) = delete; + var& operator= (const void*) = delete; }; /** Compares the values of two var objects, using the var::equals() comparison. */ diff --git a/JuceLibraryCode/modules/juce_core/files/juce_File.cpp b/JuceLibraryCode/modules/juce_core/files/juce_File.cpp index f226ba0..7041ecd 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_File.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_File.cpp @@ -104,6 +104,26 @@ static String removeEllipsis (const String& path) return path; } +static String normaliseSeparators (const String& path) +{ + auto normalisedPath = path; + + String separator (File::getSeparatorString()); + String doubleSeparator (separator + separator); + + auto uncPath = normalisedPath.startsWith (doubleSeparator) + && ! normalisedPath.fromFirstOccurrenceOf (doubleSeparator, false, false).startsWith (separator); + + if (uncPath) + normalisedPath = normalisedPath.fromFirstOccurrenceOf (doubleSeparator, false, false); + + while (normalisedPath.contains (doubleSeparator)) + normalisedPath = normalisedPath.replace (doubleSeparator, separator); + + return uncPath ? doubleSeparator + normalisedPath + : normalisedPath; +} + bool File::isRoot() const { return fullPath.isNotEmpty() && *this == getParentDirectory(); @@ -114,9 +134,9 @@ String File::parseAbsolutePath (const String& p) if (p.isEmpty()) return {}; -#if JUCE_WINDOWS + #if JUCE_WINDOWS // Windows.. - auto path = removeEllipsis (p.replaceCharacter ('/', '\\')); + auto path = normaliseSeparators (removeEllipsis (p.replaceCharacter ('/', '\\'))); if (path.startsWithChar (getSeparatorChar())) { @@ -147,7 +167,7 @@ String File::parseAbsolutePath (const String& p) return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName(); } -#else + #else // Mac or Linux.. // Yes, I know it's legal for a unix pathname to contain a backslash, but this assertion is here @@ -155,7 +175,7 @@ String File::parseAbsolutePath (const String& p) // If that's why you've ended up here, use File::getChildFile() to build your paths instead. jassert ((! p.containsChar ('\\')) || (p.indexOfChar ('/') >= 0 && p.indexOfChar ('/') < p.indexOfChar ('\\'))); - auto path = removeEllipsis (p); + auto path = normaliseSeparators (removeEllipsis (p)); if (path.startsWithChar ('~')) { @@ -196,7 +216,7 @@ String File::parseAbsolutePath (const String& p) return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName(); } -#endif + #endif while (path.endsWithChar (getSeparatorChar()) && path != getSeparatorString()) // careful not to turn a single "/" into an empty string. path = path.dropLastCharacters (1); @@ -1004,13 +1024,16 @@ MemoryMappedFile::MemoryMappedFile (const File& file, const Range& fileRa } +//============================================================================== //============================================================================== #if JUCE_UNIT_TESTS class FileTests : public UnitTest { public: - FileTests() : UnitTest ("Files", "Files") {} + FileTests() + : UnitTest ("Files", UnitTestCategories::files) + {} void runTest() override { @@ -1028,7 +1051,6 @@ public: expect (home.isDirectory()); expect (home.exists()); expect (! home.existsAsFile()); - expect (File::getSpecialLocation (File::userDocumentsDirectory).isDirectory()); expect (File::getSpecialLocation (File::userApplicationDataDirectory).isDirectory()); expect (File::getSpecialLocation (File::currentExecutableFile).exists()); expect (File::getSpecialLocation (File::currentApplicationFile).exists()); @@ -1195,6 +1217,28 @@ public: expect (demoFolder.deleteRecursively()); expect (! demoFolder.exists()); + + { + URL url ("https://audio.dev/foo/bar/"); + expectEquals (url.toString (false), String ("https://audio.dev/foo/bar/")); + expectEquals (url.getChildURL ("x").toString (false), String ("https://audio.dev/foo/bar/x")); + expectEquals (url.getParentURL().toString (false), String ("https://audio.dev/foo")); + expectEquals (url.getParentURL().getParentURL().toString (false), String ("https://audio.dev/")); + expectEquals (url.getParentURL().getParentURL().getParentURL().toString (false), String ("https://audio.dev/")); + expectEquals (url.getParentURL().getChildURL ("x").toString (false), String ("https://audio.dev/foo/x")); + expectEquals (url.getParentURL().getParentURL().getParentURL().getChildURL ("x").toString (false), String ("https://audio.dev/x")); + } + + { + URL url ("https://audio.dev/foo/bar"); + expectEquals (url.toString (false), String ("https://audio.dev/foo/bar")); + expectEquals (url.getChildURL ("x").toString (false), String ("https://audio.dev/foo/bar/x")); + expectEquals (url.getParentURL().toString (false), String ("https://audio.dev/foo")); + expectEquals (url.getParentURL().getParentURL().toString (false), String ("https://audio.dev/")); + expectEquals (url.getParentURL().getParentURL().getParentURL().toString (false), String ("https://audio.dev/")); + expectEquals (url.getParentURL().getChildURL ("x").toString (false), String ("https://audio.dev/foo/x")); + expectEquals (url.getParentURL().getParentURL().getParentURL().getChildURL ("x").toString (false), String ("https://audio.dev/x")); + } } }; diff --git a/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.cpp b/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.cpp index c659bd8..8f17e99 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.cpp @@ -76,13 +76,15 @@ bool FileInputStream::setPosition (int64 pos) return currentPosition == pos; } + +//============================================================================== //============================================================================== #if JUCE_UNIT_TESTS struct FileInputStreamTests : public UnitTest { FileInputStreamTests() - : UnitTest ("FileInputStream", "Streams") + : UnitTest ("FileInputStream", UnitTestCategories::streams) {} void runTest() override diff --git a/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.cpp b/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.cpp index 4d8e7b4..8485283 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.cpp @@ -79,6 +79,9 @@ bool FileOutputStream::write (const void* const src, const size_t numBytes) { jassert (src != nullptr && ((ssize_t) numBytes) >= 0); + if (! openedOk()) + return false; + if (bytesInBuffer + numBytes < bufferSize) { memcpy (buffer + bytesInBuffer, src, numBytes); diff --git a/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.h b/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.h index d6f4ce7..505c1ff 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.h @@ -54,7 +54,7 @@ public: */ WildcardFileFilter (const String& fileWildcardPatterns, const String& directoryWildcardPatterns, - const String& description); + const String& filterDescription); /** Destructor. */ ~WildcardFileFilter() override; diff --git a/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.cpp b/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.cpp index 7e8d221..b083742 100644 --- a/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.cpp +++ b/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.cpp @@ -25,34 +25,76 @@ namespace juce struct JSONParser { - static Result parseObjectOrArray (String::CharPointerType t, var& result) - { - t = t.findEndOfWhitespace(); + JSONParser (String::CharPointerType text) : startLocation (text), currentLocation (text) {} - switch (t.getAndAdvance()) + String::CharPointerType startLocation, currentLocation; + + struct ErrorException + { + String message; + int line = 1, column = 1; + + String getDescription() const { return String (line) + ":" + String (column) + ": error: " + message; } + Result getResult() const { return Result::fail (getDescription()); } + }; + + [[noreturn]] void throwError (juce::String message, String::CharPointerType location) + { + ErrorException e; + e.message = std::move (message); + + for (auto i = startLocation; i < location && ! i.isEmpty(); ++i) { - case 0: result = var(); return Result::ok(); - case '{': return parseObject (t, result); - case '[': return parseArray (t, result); + ++e.column; + if (*i == '\n') { e.column = 1; e.line++; } } - return createFail ("Expected '{' or '['", &t); + throw e; } - static Result parseString (const juce_wchar quoteChar, String::CharPointerType& t, var& result) + void skipWhitespace() { currentLocation = currentLocation.findEndOfWhitespace(); } + juce_wchar readChar() { return currentLocation.getAndAdvance(); } + juce_wchar peekChar() const { return *currentLocation; } + bool matchIf (char c) { if (peekChar() == (juce_wchar) c) { ++currentLocation; return true; } return false; } + bool isEOF() const { return peekChar() == 0; } + + bool matchString (const char* t) + { + while (*t != 0) + if (! matchIf (*t++)) + return false; + + return true; + } + + var parseObjectOrArray() + { + skipWhitespace(); + + if (matchIf ('{')) return parseObject(); + if (matchIf ('[')) return parseArray(); + + if (! isEOF()) + throwError ("Expected '{' or '['", currentLocation); + + return {}; + } + + String parseString (const juce_wchar quoteChar) { MemoryOutputStream buffer (256); for (;;) { - auto c = t.getAndAdvance(); + auto c = readChar(); if (c == quoteChar) break; if (c == '\\') { - c = t.getAndAdvance(); + auto errorLocation = currentLocation; + c = readChar(); switch (c) { @@ -74,10 +116,10 @@ struct JSONParser for (int i = 4; --i >= 0;) { - auto digitValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); + auto digitValue = CharacterFunctions::getHexDigitValue (readChar()); if (digitValue < 0) - return createFail ("Syntax error in unicode escape sequence"); + throwError ("Syntax error in unicode escape sequence", errorLocation); c = (juce_wchar) ((c << 4) + static_cast (digitValue)); } @@ -88,95 +130,71 @@ struct JSONParser } if (c == 0) - return createFail ("Unexpected end-of-input in string constant"); + throwError ("Unexpected EOF in string constant", currentLocation); buffer.appendUTF8Char (c); } - result = buffer.toUTF8(); - return Result::ok(); + return buffer.toUTF8(); } - static Result parseAny (String::CharPointerType& t, var& result) + var parseAny() { - t = t.findEndOfWhitespace(); - auto t2 = t; + skipWhitespace(); + auto originalLocation = currentLocation; - switch (t2.getAndAdvance()) + switch (readChar()) { - case '{': t = t2; return parseObject (t, result); - case '[': t = t2; return parseArray (t, result); - case '"': t = t2; return parseString ('"', t, result); - case '\'': t = t2; return parseString ('\'', t, result); + case '{': return parseObject(); + case '[': return parseArray(); + case '"': return parseString ('"'); + case '\'': return parseString ('\''); case '-': - t2 = t2.findEndOfWhitespace(); - if (! CharacterFunctions::isDigit (*t2)) - break; - - t = t2; - return parseNumber (t, result, true); + skipWhitespace(); + return parseNumber (true); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - return parseNumber (t, result, false); + currentLocation = originalLocation; + return parseNumber (false); case 't': // "true" - if (t2.getAndAdvance() == 'r' && t2.getAndAdvance() == 'u' && t2.getAndAdvance() == 'e') - { - t = t2; - result = var (true); - return Result::ok(); - } + if (matchString ("rue")) + return var (true); + break; case 'f': // "false" - if (t2.getAndAdvance() == 'a' && t2.getAndAdvance() == 'l' - && t2.getAndAdvance() == 's' && t2.getAndAdvance() == 'e') - { - t = t2; - result = var (false); - return Result::ok(); - } + if (matchString ("alse")) + return var (false); + break; case 'n': // "null" - if (t2.getAndAdvance() == 'u' && t2.getAndAdvance() == 'l' && t2.getAndAdvance() == 'l') - { - t = t2; - result = var(); - return Result::ok(); - } + if (matchString ("ull")) + return {}; + break; default: break; } - return createFail ("Syntax error", &t); + throwError ("Syntax error", originalLocation); } -private: - static Result createFail (const char* const message, const String::CharPointerType* location = nullptr) + var parseNumber (bool isNegative) { - String m (message); - if (location != nullptr) - m << ": \"" << String (*location, 20) << '"'; + auto originalPos = currentLocation; - return Result::fail (m); - } - - static Result parseNumber (String::CharPointerType& t, var& result, const bool isNegative) - { - auto oldT = t; - - int64 intValue = t.getAndAdvance() - '0'; + int64 intValue = readChar() - '0'; jassert (intValue >= 0 && intValue < 10); for (;;) { - auto previousChar = t; - auto c = t.getAndAdvance(); + auto lastPos = currentLocation; + auto c = readChar(); auto digit = ((int) c) - '0'; if (isPositiveAndBelow (digit, 10)) @@ -187,138 +205,99 @@ private: if (c == 'e' || c == 'E' || c == '.') { - t = oldT; - auto asDouble = CharacterFunctions::readDoubleValue (t); - result = isNegative ? -asDouble : asDouble; - return Result::ok(); + currentLocation = originalPos; + auto asDouble = CharacterFunctions::readDoubleValue (currentLocation); + return var (isNegative ? -asDouble : asDouble); } if (CharacterFunctions::isWhitespace (c) || c == ',' || c == '}' || c == ']' || c == 0) { - t = previousChar; + currentLocation = lastPos; break; } - return createFail ("Syntax error in number", &oldT); + throwError ("Syntax error in number", lastPos); } auto correctedValue = isNegative ? -intValue : intValue; - if ((intValue >> 31) != 0) - result = correctedValue; - else - result = (int) correctedValue; - - return Result::ok(); + return (intValue >> 31) != 0 ? var (correctedValue) + : var ((int) correctedValue); } - static Result parseObject (String::CharPointerType& t, var& result) + var parseObject() { auto resultObject = new DynamicObject(); - result = resultObject; + var result (resultObject); auto& resultProperties = resultObject->getProperties(); + auto startOfObjectDecl = currentLocation; for (;;) { - t = t.findEndOfWhitespace(); - - auto oldT = t; - auto c = t.getAndAdvance(); + skipWhitespace(); + auto errorLocation = currentLocation; + auto c = readChar(); if (c == '}') break; if (c == 0) - return createFail ("Unexpected end-of-input in object declaration"); + throwError ("Unexpected EOF in object declaration", startOfObjectDecl); - if (c == '"') - { - var propertyNameVar; - auto r = parseString ('"', t, propertyNameVar); + if (c != '"') + throwError ("Expected a property name in double-quotes", errorLocation); - if (r.failed()) - return r; + errorLocation = currentLocation; + Identifier propertyName (parseString ('"')); - const Identifier propertyName (propertyNameVar.toString()); + if (! propertyName.isValid()) + throwError ("Invalid property name", errorLocation); - if (propertyName.isValid()) - { - t = t.findEndOfWhitespace(); - oldT = t; + skipWhitespace(); + errorLocation = currentLocation; - auto c2 = t.getAndAdvance(); + if (readChar() != ':') + throwError ("Expected ':'", errorLocation); - if (c2 != ':') - return createFail ("Expected ':', but found", &oldT); + resultProperties.set (propertyName, parseAny()); - resultProperties.set (propertyName, var()); - var* propertyValue = resultProperties.getVarPointer (propertyName); + skipWhitespace(); + if (matchIf (',')) continue; + if (matchIf ('}')) break; - auto r2 = parseAny (t, *propertyValue); - - if (r2.failed()) - return r2; - - t = t.findEndOfWhitespace(); - oldT = t; - - auto nextChar = t.getAndAdvance(); - - if (nextChar == ',') - continue; - - if (nextChar == '}') - break; - } - } - - return createFail ("Expected object member declaration, but found", &oldT); + throwError ("Expected ',' or '}'", currentLocation); } - return Result::ok(); + return result; } - static Result parseArray (String::CharPointerType& t, var& result) + var parseArray() { - result = var (Array()); - auto* destArray = result.getArray(); + auto result = var (Array()); + auto destArray = result.getArray(); + auto startOfArrayDecl = currentLocation; for (;;) { - t = t.findEndOfWhitespace(); + skipWhitespace(); - auto oldT = t; - auto c = t.getAndAdvance(); - - if (c == ']') + if (matchIf (']')) break; - if (c == 0) - return createFail ("Unexpected end-of-input in array declaration"); + if (isEOF()) + throwError ("Unexpected EOF in array declaration", startOfArrayDecl); - t = oldT; - destArray->add (var()); - auto r = parseAny (t, destArray->getReference (destArray->size() - 1)); + destArray->add (parseAny()); + skipWhitespace(); - if (r.failed()) - return r; + if (matchIf (',')) continue; + if (matchIf (']')) break; - t = t.findEndOfWhitespace(); - oldT = t; - - auto nextChar = t.getAndAdvance(); - - if (nextChar == ',') - continue; - - if (nextChar == ']') - break; - - return createFail ("Expected object array item, but found", &oldT); + throwError ("Expected ',' or ']'", currentLocation); } - return Result::ok(); + return result; } }; @@ -478,20 +457,21 @@ var JSON::parse (const String& text) { var result; - if (! parse (text, result)) - result = var(); + if (parse (text, result)) + return result; - return result; + return {}; } var JSON::fromString (StringRef text) { - var result; + try + { + return JSONParser (text.text).parseAny(); + } + catch (const JSONParser::ErrorException&) {} - if (! JSONParser::parseAny (text.text, result)) - result = var(); - - return result; + return {}; } var JSON::parse (InputStream& input) @@ -506,7 +486,16 @@ var JSON::parse (const File& file) Result JSON::parse (const String& text, var& result) { - return JSONParser::parseObjectOrArray (text.getCharPointer(), result); + try + { + result = JSONParser (text.getCharPointer()).parseObjectOrArray(); + } + catch (const JSONParser::ErrorException& error) + { + return error.getResult(); + } + + return Result::ok(); } String JSON::toString (const var& data, const bool allOnOneLine, int maximumDecimalPlaces) @@ -530,14 +519,26 @@ String JSON::escapeString (StringRef s) Result JSON::parseQuotedString (String::CharPointerType& t, var& result) { - auto quote = t.getAndAdvance(); + try + { + JSONParser parser (t); + auto quote = parser.readChar(); - if (quote == '"' || quote == '\'') - return JSONParser::parseString (quote, t, result); + if (quote != '"' && quote != '\'') + return Result::fail ("Not a quoted string!"); - return Result::fail ("Not a quoted string!"); + result = parser.parseString (quote); + t = parser.currentLocation; + } + catch (const JSONParser::ErrorException& error) + { + return error.getResult(); + } + + return Result::ok(); } + //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS @@ -545,7 +546,9 @@ Result JSON::parseQuotedString (String::CharPointerType& t, var& result) class JSONTests : public UnitTest { public: - JSONTests() : UnitTest ("JSON", "JSON") {} + JSONTests() + : UnitTest ("JSON", UnitTestCategories::json) + {} static String createRandomWideCharString (Random& r) { @@ -611,7 +614,7 @@ public: case 7: { - DynamicObject* o = new DynamicObject(); + auto o = new DynamicObject(); for (int i = r.nextInt (30); --i >= 0;) o->setProperty (createRandomIdentifier (r), createRandomVar (r, depth + 1)); @@ -629,7 +632,7 @@ public: { beginTest ("JSON"); - Random r = getRandom(); + auto r = getRandom(); expect (JSON::parse (String()) == var()); expect (JSON::parse ("{}").isObject()); diff --git a/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.cpp b/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.cpp index 720f525..4a92686 100644 --- a/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.cpp +++ b/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.cpp @@ -816,7 +816,8 @@ struct JavascriptEngine::RootObject : public DynamicObject for (int i = 0; i < values.size(); ++i) a.add (values.getUnchecked(i)->getResult (s)); - return a; + // std::move() needed here for older compilers + return std::move (a); } OwnedArray values; @@ -1183,7 +1184,7 @@ struct JavascriptEngine::RootObject : public DynamicObject if (matchIf (TokenTypes::comma)) { std::unique_ptr block (new BlockStatement (location)); - block->statements.add (s.release()); + block->statements.add (std::move (s)); block->statements.add (parseVar()); return block.release(); } @@ -1625,7 +1626,8 @@ struct JavascriptEngine::RootObject : public DynamicObject for (int i = 2; i < a.numArguments; ++i) array->insert (start++, get (a, i)); - return itemsRemoved; + // std::move() needed here for older compilers + return std::move (itemsRemoved); } return var::undefined(); diff --git a/JuceLibraryCode/modules/juce_core/juce_core.cpp b/JuceLibraryCode/modules/juce_core/juce_core.cpp index 2e7c37c..a6a2ef7 100644 --- a/JuceLibraryCode/modules/juce_core/juce_core.cpp +++ b/JuceLibraryCode/modules/juce_core/juce_core.cpp @@ -48,7 +48,11 @@ #if JUCE_WINDOWS #include - #if ! JUCE_MINGW + #if JUCE_MINGW + #include + #include + #include + #else #pragma warning (push) #pragma warning (disable: 4091) #include @@ -59,12 +63,6 @@ #endif #endif - #if JUCE_MINGW - #include - #include - #include - #endif - #else #if JUCE_LINUX || JUCE_ANDROID #include @@ -102,6 +100,7 @@ #if JUCE_MAC || JUCE_IOS #include #include + #include #endif #if JUCE_ANDROID @@ -140,7 +139,6 @@ #include "misc/juce_RuntimePermissions.cpp" #include "misc/juce_Result.cpp" #include "misc/juce_Uuid.cpp" -#include "misc/juce_StdFunctionCompat.cpp" #include "misc/juce_ConsoleApplication.cpp" #include "network/juce_MACAddress.cpp" #include "network/juce_NamedPipe.cpp" @@ -185,63 +183,63 @@ //============================================================================== #if ! JUCE_WINDOWS -#include "native/juce_posix_SharedCode.h" -#include "native/juce_posix_NamedPipe.cpp" -#if ! JUCE_ANDROID || __ANDROID_API__ >= 24 - #include "native/juce_posix_IPAddress.h" -#endif + #include "native/juce_posix_SharedCode.h" + #include "native/juce_posix_NamedPipe.cpp" + #if ! JUCE_ANDROID || __ANDROID_API__ >= 24 + #include "native/juce_posix_IPAddress.h" + #endif #endif //============================================================================== #if JUCE_MAC || JUCE_IOS -#include "native/juce_mac_Files.mm" -#include "native/juce_mac_Network.mm" -#include "native/juce_mac_Strings.mm" -#include "native/juce_mac_SystemStats.mm" -#include "native/juce_mac_Threads.mm" + #include "native/juce_mac_Files.mm" + #include "native/juce_mac_Network.mm" + #include "native/juce_mac_Strings.mm" + #include "native/juce_mac_SystemStats.mm" + #include "native/juce_mac_Threads.mm" //============================================================================== #elif JUCE_WINDOWS -#include "native/juce_win32_Files.cpp" -#include "native/juce_win32_Network.cpp" -#include "native/juce_win32_Registry.cpp" -#include "native/juce_win32_SystemStats.cpp" -#include "native/juce_win32_Threads.cpp" + #include "native/juce_win32_Files.cpp" + #include "native/juce_win32_Network.cpp" + #include "native/juce_win32_Registry.cpp" + #include "native/juce_win32_SystemStats.cpp" + #include "native/juce_win32_Threads.cpp" //============================================================================== #elif JUCE_LINUX -#include "native/juce_linux_CommonFile.cpp" -#include "native/juce_linux_Files.cpp" -#include "native/juce_linux_Network.cpp" -#if JUCE_USE_CURL - #include "native/juce_curl_Network.cpp" -#endif -#include "native/juce_linux_SystemStats.cpp" -#include "native/juce_linux_Threads.cpp" + #include "native/juce_linux_CommonFile.cpp" + #include "native/juce_linux_Files.cpp" + #include "native/juce_linux_Network.cpp" + #if JUCE_USE_CURL + #include "native/juce_curl_Network.cpp" + #endif + #include "native/juce_linux_SystemStats.cpp" + #include "native/juce_linux_Threads.cpp" //============================================================================== #elif JUCE_ANDROID - -#include "native/juce_linux_CommonFile.cpp" -#include "native/juce_android_JNIHelpers.cpp" -#include "native/juce_android_Files.cpp" -#include "native/juce_android_Misc.cpp" -#include "native/juce_android_Network.cpp" -#include "native/juce_android_SystemStats.cpp" -#include "native/juce_android_Threads.cpp" -#include "native/juce_android_RuntimePermissions.cpp" + #include "native/juce_linux_CommonFile.cpp" + #include "native/juce_android_JNIHelpers.cpp" + #include "native/juce_android_Files.cpp" + #include "native/juce_android_Misc.cpp" + #include "native/juce_android_Network.cpp" + #include "native/juce_android_SystemStats.cpp" + #include "native/juce_android_Threads.cpp" + #include "native/juce_android_RuntimePermissions.cpp" #endif #include "threads/juce_ChildProcess.cpp" #include "threads/juce_HighResolutionTimer.cpp" +#include "threads/juce_WaitableEvent.cpp" #include "network/juce_URL.cpp" #include "network/juce_WebInputStream.cpp" #include "streams/juce_URLInputSource.cpp" //============================================================================== #if JUCE_UNIT_TESTS -#include "containers/juce_HashMap_test.cpp" + #include "containers/juce_HashMap_test.cpp" #endif //============================================================================== diff --git a/JuceLibraryCode/modules/juce_core/juce_core.h b/JuceLibraryCode/modules/juce_core/juce_core.h index 877de40..62d0dfa 100644 --- a/JuceLibraryCode/modules/juce_core/juce_core.h +++ b/JuceLibraryCode/modules/juce_core/juce_core.h @@ -30,19 +30,19 @@ BEGIN_JUCE_MODULE_DECLARATION - ID: juce_core - vendor: juce - version: 5.4.3 - name: JUCE core classes - description: The essential set of basic JUCE classes, as required by all the other JUCE modules. Includes text, container, memory, threading and i/o functionality. - website: http://www.juce.com/juce - license: ISC + ID: juce_core + vendor: juce + version: 5.4.7 + name: JUCE core classes + description: The essential set of basic JUCE classes, as required by all the other JUCE modules. Includes text, container, memory, threading and i/o functionality. + website: http://www.juce.com/juce + license: ISC dependencies: - OSXFrameworks: Cocoa IOKit - iOSFrameworks: Foundation - linuxLibs: rt dl pthread - mingwLibs: uuid wsock32 wininet version ole32 ws2_32 oleaut32 imm32 comdlg32 shlwapi rpcrt4 winmm + OSXFrameworks: Cocoa IOKit + iOSFrameworks: Foundation + linuxLibs: rt dl pthread + mingwLibs: uuid wsock32 wininet version ole32 ws2_32 oleaut32 imm32 comdlg32 shlwapi rpcrt4 winmm END_JUCE_MODULE_DECLARATION @@ -107,7 +107,7 @@ //============================================================================== /** Config: JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES - In a Visual C++ build, this can be used to stop the required system libs being + In a Windows build, this can be used to stop the required system libs being automatically added to the link stage. */ #ifndef JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES @@ -134,19 +134,15 @@ Enables http/https support via libcurl (Linux only). Enabling this will add an additional run-time dynamic dependency to libcurl. - If you disable this then https/ssl support will not be available on linux. + If you disable this then https/ssl support will not be available on Linux. */ #ifndef JUCE_USE_CURL - #if JUCE_LINUX - #define JUCE_USE_CURL 1 - #else - #define JUCE_USE_CURL 0 - #endif + #define JUCE_USE_CURL 1 #endif /** Config: JUCE_LOAD_CURL_SYMBOLS_LAZILY If enabled, JUCE will load libcurl lazily when required (for example, when WebInputStream - is used). Enabling this flag may also help with library dependency erros as linking + is used). Enabling this flag may also help with library dependency errors as linking libcurl at compile-time may instruct the linker to hard depend on a specific version of libcurl. It's also useful if you want to limit the amount of JUCE dependencies and you are not using WebInputStream or the URL classes. @@ -155,13 +151,12 @@ #define JUCE_LOAD_CURL_SYMBOLS_LAZILY 0 #endif - /** Config: JUCE_CATCH_UNHANDLED_EXCEPTIONS If enabled, this will add some exception-catching code to forward unhandled exceptions to your JUCEApplicationBase::unhandledException() callback. */ #ifndef JUCE_CATCH_UNHANDLED_EXCEPTIONS - //#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 1 + #define JUCE_CATCH_UNHANDLED_EXCEPTIONS 0 #endif /** Config: JUCE_ALLOW_STATIC_NULL_VARIABLES @@ -170,7 +165,7 @@ constructor code. */ #ifndef JUCE_ALLOW_STATIC_NULL_VARIABLES - #define JUCE_ALLOW_STATIC_NULL_VARIABLES 1 + #define JUCE_ALLOW_STATIC_NULL_VARIABLES 0 #endif /** Config: JUCE_STRICT_REFCOUNTEDPOINTER @@ -269,6 +264,8 @@ namespace juce #include "text/juce_StringPool.h" #include "text/juce_Identifier.h" #include "text/juce_StringArray.h" +#include "system/juce_SystemStats.h" +#include "memory/juce_HeavyweightLeakedObjectDetector.h" #include "text/juce_StringPairArray.h" #include "text/juce_TextDiff.h" #include "text/juce_LocalisedStrings.h" @@ -280,8 +277,6 @@ namespace juce #include "containers/juce_NamedValueSet.h" #include "containers/juce_DynamicObject.h" #include "containers/juce_HashMap.h" -#include "system/juce_SystemStats.h" -#include "memory/juce_HeavyweightLeakedObjectDetector.h" #include "time/juce_RelativeTime.h" #include "time/juce_Time.h" #include "streams/juce_InputStream.h" @@ -353,6 +348,10 @@ namespace juce #include "native/juce_android_JNIHelpers.h" #endif +#if JUCE_UNIT_TESTS + #include "unit_tests/juce_UnitTestCategories.h" +#endif + #ifndef DOXYGEN namespace juce { diff --git a/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.h b/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.h index b6b1074..50e5c87 100644 --- a/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.h +++ b/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.h @@ -83,7 +83,7 @@ public: The filename used is based on the root and suffix strings provided, along with a time and date string, meaning that a new, empty log file will be always be created - rather than appending to an exising one. + rather than appending to an existing one. The method might return nullptr if the file can't be created for some reason. diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.cpp b/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.cpp index dda62ad..fae2e2f 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.cpp +++ b/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.cpp @@ -1196,7 +1196,7 @@ void BigInteger::loadFromMemoryBlock (const MemoryBlock& data) auto* values = ensureSize (numInts); for (int i = 0; i < (int) numInts - 1; ++i) - values[i] = (uint32) ByteOrder::littleEndianInt (addBytesToPointer (data.getData(), sizeof (uint32) * (size_t) i)); + values[i] = (uint32) ByteOrder::littleEndianInt (addBytesToPointer (data.getData(), (size_t) i * sizeof (uint32))); values[numInts - 1] = 0; @@ -1240,7 +1240,7 @@ void writeLittleEndianBitsInBuffer (void* buffer, uint32 startBit, uint32 numBit } if (numBits > 0) - *data = (uint8) ((*data & (0xff << numBits)) | value); + *data = (uint8) ((*data & (uint32) (0xff << numBits)) | value); } uint32 readLittleEndianBitsInBuffer (const void* buffer, uint32 startBit, uint32 numBits) noexcept @@ -1255,7 +1255,7 @@ uint32 readLittleEndianBitsInBuffer (const void* buffer, uint32 startBit, uint32 if (const uint32 offset = (startBit & 7)) { const uint32 bitsInByte = 8 - offset; - result = (*data >> offset); + result = (uint32) (*data >> offset); if (bitsInByte >= numBits) return result & ((1u << numBits) - 1u); @@ -1278,6 +1278,7 @@ uint32 readLittleEndianBitsInBuffer (const void* buffer, uint32 startBit, uint32 return result; } + //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS @@ -1285,7 +1286,9 @@ uint32 readLittleEndianBitsInBuffer (const void* buffer, uint32 startBit, uint32 class BigIntegerTests : public UnitTest { public: - BigIntegerTests() : UnitTest ("BigInteger", "Maths") {} + BigIntegerTests() + : UnitTest ("BigInteger", UnitTestCategories::maths) + {} static BigInteger getBigRandom (Random& r) { diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h b/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h index d673693..452a03d 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h +++ b/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h @@ -296,13 +296,8 @@ void ignoreUnused (Types&&...) noexcept {} int numElements = numElementsInArray (myArray) // returns 3 @endcode */ -template -int numElementsInArray (Type (&array)[N]) -{ - (void) array; - (void) sizeof (0[array]); // This line should cause an error if you pass an object with a user-defined subscript operator - return N; -} +template +JUCE_CONSTEXPR int numElementsInArray (Type (&)[N]) noexcept { return N; } //============================================================================== // Some useful maths functions that aren't always present with all compilers and build settings. @@ -331,12 +326,6 @@ inline float juce_hypot (float a, float b) noexcept } #endif -#if JUCE_MSVC && ! defined (DOXYGEN) // The MSVC libraries omit these functions for some reason... - template Type asinh (Type x) { return std::log (x + std::sqrt (x * x + (Type) 1)); } - template Type acosh (Type x) { return std::log (x + std::sqrt (x * x - (Type) 1)); } - template Type atanh (Type x) { return (std::log (x + (Type) 1) - std::log (((Type) 1) - x)) / (Type) 2; } -#endif - //============================================================================== #if JUCE_HAS_CONSTEXPR diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_NormalisableRange.h b/JuceLibraryCode/modules/juce_core/maths/juce_NormalisableRange.h index edda39a..eb2c8d9 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_NormalisableRange.h +++ b/JuceLibraryCode/modules/juce_core/maths/juce_NormalisableRange.h @@ -44,32 +44,8 @@ public: NormalisableRange (const NormalisableRange&) = default; NormalisableRange& operator= (const NormalisableRange&) = default; - - // VS2013 can't default move constructors - NormalisableRange (NormalisableRange&& other) - : start (other.start), end (other.end), - interval (other.interval), skew (other.skew), - symmetricSkew (other.symmetricSkew), - convertFrom0To1Function (std::move (other.convertFrom0To1Function)), - convertTo0To1Function (std::move (other.convertTo0To1Function)), - snapToLegalValueFunction (std::move (other.snapToLegalValueFunction)) - { - } - - // VS2013 can't default move assignments - NormalisableRange& operator= (NormalisableRange&& other) - { - start = other.start; - end = other.end; - interval = other.interval; - skew = other.skew; - symmetricSkew = other.symmetricSkew; - convertFrom0To1Function = std::move (other.convertFrom0To1Function); - convertTo0To1Function = std::move (other.convertTo0To1Function); - snapToLegalValueFunction = std::move (other.snapToLegalValueFunction); - - return *this; - } + NormalisableRange (NormalisableRange&&) = default; + NormalisableRange& operator= (NormalisableRange&&) = default; /** Creates a NormalisableRange with a given range, interval and skew factor. */ NormalisableRange (ValueType rangeStart, @@ -112,6 +88,11 @@ public: { } + /** A function object which can remap a value in some way based on the start and end of a range. */ + using ValueRemapFunction = std::function; + /** Creates a NormalisableRange with a given range and an injective mapping function. @param rangeStart The minimum value in the range. @@ -125,14 +106,14 @@ public: */ NormalisableRange (ValueType rangeStart, ValueType rangeEnd, - std::function convertFrom0To1Func, - std::function convertTo0To1Func, - std::function snapToLegalValueFunc = nullptr) noexcept + ValueRemapFunction convertFrom0To1Func, + ValueRemapFunction convertTo0To1Func, + ValueRemapFunction snapToLegalValueFunc = {}) noexcept : start (rangeStart), end (rangeEnd), - convertFrom0To1Function (convertFrom0To1Func), - convertTo0To1Function (convertTo0To1Func), - snapToLegalValueFunction (snapToLegalValueFunc) + convertFrom0To1Function (std::move (convertFrom0To1Func)), + convertTo0To1Function (std::move (convertTo0To1Func)), + snapToLegalValueFunction (std::move (snapToLegalValueFunc)) { checkInvariants(); } @@ -190,7 +171,7 @@ public: } /** Takes a non-normalised value and snaps it based on either the interval property of - this NormalisedRange or the lambda function supplied to the constructor. + this NormalisableRange or the lambda function supplied to the constructor. */ ValueType snapToLegalValue (ValueType v) const noexcept { @@ -274,11 +255,7 @@ private: return clampedValue; } - using ConversionFunction = std::function; - - ConversionFunction convertFrom0To1Function = {}, - convertTo0To1Function = {}, - snapToLegalValueFunction = {}; + ValueRemapFunction convertFrom0To1Function, convertTo0To1Function, snapToLegalValueFunction; }; } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_Random.cpp b/JuceLibraryCode/modules/juce_core/maths/juce_Random.cpp index 020252a..2142704 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_Random.cpp +++ b/JuceLibraryCode/modules/juce_core/maths/juce_Random.cpp @@ -58,7 +58,7 @@ void Random::combineSeed (const int64 seedValue) noexcept void Random::setSeedRandomly() { - static int64 globalSeed = 0; + static std::atomic globalSeed { 0 }; combineSeed (globalSeed ^ (int64) (pointer_sized_int) this); combineSeed (Time::getMillisecondCounter()); @@ -161,13 +161,17 @@ void Random::fillBitsRandomly (BigInteger& arrayToChange, int startBit, int numB arrayToChange.setBit (startBit + numBits, nextBool()); } + +//============================================================================== //============================================================================== #if JUCE_UNIT_TESTS class RandomTests : public UnitTest { public: - RandomTests() : UnitTest ("Random", "Maths") {} + RandomTests() + : UnitTest ("Random", UnitTestCategories::maths) + {} void runTest() override { diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_ContainerDeletePolicy.h b/JuceLibraryCode/modules/juce_core/memory/juce_ContainerDeletePolicy.h index bd3fd39..212b7cc 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_ContainerDeletePolicy.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_ContainerDeletePolicy.h @@ -32,7 +32,7 @@ namespace juce create a specialised version of it for a particular class if you need to delete that type of object in a more appropriate way. - @see ScopedPointer, OwnedArray + @see OwnedArray @tags{Core} */ @@ -45,8 +45,8 @@ struct ContainerDeletePolicy // an incomplete type for ObjectType (for example, a type that is declared // but not defined). This is a problem because then the following delete is // undefined behaviour. The purpose of the sizeof is to capture this situation. - // If this was caused by a ScopedPointer to a forward-declared type, move the - // implementation of all methods trying to use the ScopedPointer (e.g. the destructor + // If this was caused by a OwnedArray of a forward-declared type, move the + // implementation of all methods trying to use the OwnedArray (e.g. the destructor // of the class owning it) into cpp files where they can see to the definition // of ObjectType. This should fix the error. ignoreUnused (sizeof (ObjectType)); diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_HeavyweightLeakedObjectDetector.h b/JuceLibraryCode/modules/juce_core/memory/juce_HeavyweightLeakedObjectDetector.h index 5a336dc..53ff48f 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_HeavyweightLeakedObjectDetector.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_HeavyweightLeakedObjectDetector.h @@ -44,14 +44,14 @@ class HeavyweightLeakedObjectDetector { public: //============================================================================== - HeavyweightLeakedObjectDetector() noexcept { getBacktraceMap().set (this, SystemStats::getStackBacktrace()); } - HeavyweightLeakedObjectDetector (const HeavyweightLeakedObjectDetector&) noexcept { getBacktraceMap().set (this, SystemStats::getStackBacktrace()); } + HeavyweightLeakedObjectDetector() noexcept { getBacktraceMap()[this] = SystemStats::getStackBacktrace(); } + HeavyweightLeakedObjectDetector (const HeavyweightLeakedObjectDetector&) noexcept { getBacktraceMap()[this] = SystemStats::getStackBacktrace(); } - ~HeavyweightLeakedObjectDetector() { getBacktraceMap().remove (this); } + ~HeavyweightLeakedObjectDetector() { getBacktraceMap().erase (this); } private: //============================================================================== - typedef HashMap*, String> BacktraceMap; + typedef std::map*, String> BacktraceMap; //============================================================================== struct BacktraceMapHolder @@ -82,11 +82,11 @@ private: String str; int counter = 1; - for (typename BacktraceMap::Iterator i (map); i.next();) + for (auto& bt : map) { str << "\nBacktrace " << String (counter++) << "\n" << "-----------------------------------------------------------------" << "\n" - << i.getValue(); + << bt.second; } return str; diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_Memory.h b/JuceLibraryCode/modules/juce_core/memory/juce_Memory.h index 75d3097..6587d36 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_Memory.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_Memory.h @@ -44,7 +44,7 @@ inline void deleteAndZero (Type& pointer) { delete poi a specific number of bytes, */ template -inline Type* addBytesToPointer (Type* basePointer, IntegerType bytes) noexcept { return (Type*) (const_cast (reinterpret_cast (basePointer)) + bytes); } +inline Type* addBytesToPointer (Type* basePointer, IntegerType bytes) noexcept { return reinterpret_cast (const_cast (reinterpret_cast (basePointer)) + bytes); } /** A handy function to round up a pointer to the nearest multiple of a given number of bytes. alignmentBytes must be a power of two. */ diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.cpp b/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.cpp index 8e89984..8a87e33 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.cpp +++ b/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.cpp @@ -356,7 +356,7 @@ String MemoryBlock::toBase64Encoding() const String destString ((unsigned int) size); // store the length, followed by a '.', and then the data. auto initialLen = destString.length(); - destString.preallocateBytes (sizeof (String::CharPointerType::CharType) * (size_t) initialLen + 2 + numChars); + destString.preallocateBytes ((size_t) initialLen * sizeof (String::CharPointerType::CharType) + 2 + numChars); auto d = destString.getCharPointer(); d += initialLen; diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.h b/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.h index b942233..ceb2851 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.h @@ -88,19 +88,36 @@ public: Note that the pointer returned will probably become invalid when the block is resized. */ - void* getData() const noexcept { return data; } + void* getData() noexcept { return data; } + + /** Returns a void pointer to the data. + + Note that the pointer returned will probably become invalid when the + block is resized. + */ + const void* getData() const noexcept { return data; } /** Returns a byte from the memory block. This returns a reference, so you can also use it to set a byte. */ template - char& operator[] (const Type offset) const noexcept { return data [offset]; } + char& operator[] (const Type offset) noexcept { return data [offset]; } + + /** Returns a byte from the memory block. */ + template + const char& operator[] (const Type offset) const noexcept { return data [offset]; } /** Returns an iterator for the data. */ - char* begin() const noexcept { return data; } + char* begin() noexcept { return data; } + + /** Returns an iterator for the data. */ + const char* begin() const noexcept { return data; } /** Returns an end-iterator for the data. */ - char* end() const noexcept { return begin() + getSize(); } + char* end() noexcept { return begin() + getSize(); } + + /** Returns an end-iterator for the data. */ + const char* end() const noexcept { return begin() + getSize(); } //============================================================================== /** Returns the block's current allocated size, in bytes. */ diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_OptionalScopedPointer.h b/JuceLibraryCode/modules/juce_core/memory/juce_OptionalScopedPointer.h index fa7362c..1c21885 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_OptionalScopedPointer.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_OptionalScopedPointer.h @@ -28,11 +28,9 @@ namespace juce Holds a pointer to an object which can optionally be deleted when this pointer goes out of scope. - This acts in many ways like a ScopedPointer, but allows you to specify whether or + This acts in many ways like a std::unique_ptr, but allows you to specify whether or not the object is deleted. - @see ScopedPointer - @tags{Core} */ template @@ -46,7 +44,7 @@ public: /** Creates an OptionalScopedPointer to point to a given object, and specifying whether the OptionalScopedPointer will delete it. - If takeOwnership is true, then the OptionalScopedPointer will act like a ScopedPointer, + If takeOwnership is true, then the OptionalScopedPointer will act like a std::unique_ptr, deleting the object when it is itself deleted. If this parameter is false, then the OptionalScopedPointer just holds a normal pointer to the object, and won't delete it. */ @@ -57,7 +55,7 @@ public: /** Takes ownership of the object that another OptionalScopedPointer holds. - Like a normal ScopedPointer, the objectToTransferFrom object will become null, + Like a normal std::unique_ptr, the objectToTransferFrom object will become null, as ownership of the managed object is transferred to this object. The flag to indicate whether or not to delete the managed object is also @@ -71,7 +69,7 @@ public: /** Takes ownership of the object that another OptionalScopedPointer holds. - Like a normal ScopedPointer, the objectToTransferFrom object will become null, + Like a normal std::unique_ptr, the objectToTransferFrom object will become null, as ownership of the managed object is transferred to this object. The ownership flag that says whether or not to delete the managed object is also @@ -124,6 +122,8 @@ public: { if (! shouldDelete) object.release(); + else + object.reset(); } /** Does the same thing as reset(). */ @@ -132,7 +132,7 @@ public: /** Makes this OptionalScopedPointer point at a new object, specifying whether the OptionalScopedPointer will take ownership of the object. - If takeOwnership is true, then the OptionalScopedPointer will act like a ScopedPointer, + If takeOwnership is true, then the OptionalScopedPointer will act like a std::unique_ptr, deleting the object when it is itself deleted. If this parameter is false, then the OptionalScopedPointer just holds a normal pointer to the object, and won't delete it. */ @@ -176,14 +176,8 @@ public: private: //============================================================================== - ScopedPointer object; + std::unique_ptr object; bool shouldDelete = false; - - // This is here to avoid people accidentally taking a second owned copy of - // a scoped pointer, which is almost certainly not what you intended to do! - // If you hit a problem with this, you probably meant to say - // myPointer.setOwned (myScopedPointer.release()) - void setOwned (const ScopedPointer&) = delete; }; } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_ReferenceCountedObject.h b/JuceLibraryCode/modules/juce_core/memory/juce_ReferenceCountedObject.h index 617d2d0..575687d 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_ReferenceCountedObject.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_ReferenceCountedObject.h @@ -49,9 +49,14 @@ namespace juce Once a new ReferenceCountedObject has been assigned to a pointer, be careful not to delete the object manually. - This class uses an Atomic value to hold the reference count, so that - the pointers can be passed between threads safely. For a faster but non-thread-safe - version, use SingleThreadedReferenceCountedObject instead. + This class uses an Atomic value to hold the reference count, so + the reference count can be updated on multiple threads. Note that + whilst it's thread-safe to create and delete a ReferenceCountedObjectPtr + to a ReferenceCountedObject shared between threads, it's not thread-safe + to modify or swap the ReferenceCountedObject. + + For a faster but non-thread-safe version, use SingleThreadedReferenceCountedObject + instead. @see ReferenceCountedObjectPtr, ReferenceCountedArray, SingleThreadedReferenceCountedObject diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_ScopedPointer.h b/JuceLibraryCode/modules/juce_core/memory/juce_ScopedPointer.h index 977d2e0..1ca6ff8 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_ScopedPointer.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_ScopedPointer.h @@ -20,82 +20,41 @@ ============================================================================== */ +#ifndef DOXYGEN + namespace juce { //============================================================================== /** This class is deprecated. You should use std::unique_ptr instead. - - - A ScopedPointer holds a pointer that is automatically deleted when the ScopedPointer - goes out of scope. - - Once a pointer has been passed to a ScopedPointer, it will make sure that the pointer - gets deleted when the ScopedPointer is deleted. Using the ScopedPointer on the stack or - as member variables is a good way to use RAII to avoid accidentally leaking dynamically - created objects. - - A ScopedPointer can be used in pretty much the same way that you'd use a normal pointer - to an object. If you use the assignment operator to assign a different object to a - ScopedPointer, the old one will be automatically deleted. - - Important note: The class is designed to hold a pointer to an object, NOT to an array! - It calls delete on its payload, not delete[], so do not give it an array to hold! For - that kind of purpose, you should be using HeapBlock or Array instead. - - A const ScopedPointer is guaranteed not to lose ownership of its object or change the - object to which it points during its lifetime. This means that making a copy of a const - ScopedPointer is impossible, as that would involve the new copy taking ownership from the - old one. - - If you need to get a pointer out of a ScopedPointer without it being deleted, you - can use the release() method. - - @tags{Core} */ template class ScopedPointer { public: //============================================================================== - /** Creates a ScopedPointer containing a null pointer. */ - inline ScopedPointer() = default; + // ScopedPointer is deprecated! You should use std::unique_ptr instead. + JUCE_DEPRECATED_ATTRIBUTE inline ScopedPointer() = default; - /** Creates a ScopedPointer containing a null pointer. */ - inline ScopedPointer (decltype (nullptr)) noexcept {} + // ScopedPointer is deprecated! You should use std::unique_ptr instead. + JUCE_DEPRECATED_ATTRIBUTE inline ScopedPointer (decltype (nullptr)) noexcept {} - /** Creates a ScopedPointer that owns the specified object. */ - inline ScopedPointer (ObjectType* objectToTakePossessionOf) noexcept + // ScopedPointer is deprecated! You should use std::unique_ptr instead. + JUCE_DEPRECATED_ATTRIBUTE inline ScopedPointer (ObjectType* objectToTakePossessionOf) noexcept : object (objectToTakePossessionOf) { } - /** Creates a ScopedPointer that takes its pointer from another ScopedPointer. - - Because a pointer can only belong to one ScopedPointer, this transfers - the pointer from the other object to this one, and the other object is reset to - be a null pointer. - */ + // ScopedPointer is deprecated! You should use std::unique_ptr instead. ScopedPointer (ScopedPointer& objectToTransferFrom) noexcept : object (objectToTransferFrom.release()) { } - /** Destructor. - If the ScopedPointer currently refers to an object, it'll be deleted. - */ - inline ~ScopedPointer() { reset(); } + // ScopedPointer is deprecated! You should use std::unique_ptr instead. + JUCE_DEPRECATED_ATTRIBUTE inline ~ScopedPointer() { reset(); } - /** Changes this ScopedPointer to point to a new object. - - Because a pointer can only belong to one ScopedPointer, this transfers - the pointer from the other object to this one, and the other object is reset to - be a null pointer. - - If this ScopedPointer already points to an object, that object - will first be deleted. - */ ScopedPointer& operator= (ScopedPointer& objectToTransferFrom) { if (this != objectToTransferFrom.getAddress()) @@ -109,23 +68,17 @@ public: return *this; } - /** Changes this ScopedPointer to point to a new object. - If this ScopedPointer already points to an object, that object will first be deleted. - The pointer that you pass in may be a nullptr. - */ ScopedPointer& operator= (ObjectType* newObjectToTakePossessionOf) { reset (newObjectToTakePossessionOf); return *this; } - /** Take ownership of another ScopedPointer */ ScopedPointer (ScopedPointer&& other) noexcept : object (other.object) { other.object = nullptr; } - /** Take ownership of another ScopedPointer */ ScopedPointer& operator= (ScopedPointer&& other) noexcept { reset (other.release()); @@ -133,20 +86,11 @@ public: } //============================================================================== - /** Returns the object that this ScopedPointer refers to. */ inline operator ObjectType*() const noexcept { return object; } - - /** Returns the object that this ScopedPointer refers to. */ inline ObjectType* get() const noexcept { return object; } - - /** Returns the object that this ScopedPointer refers to. */ inline ObjectType& operator*() const noexcept { return *object; } - - /** Lets you access methods and properties of the object that this ScopedPointer refers to. */ inline ObjectType* operator->() const noexcept { return object; } - //============================================================================== - /** Clears this pointer, deleting the object it points to if there is one. */ void reset() { auto* oldObject = object; @@ -154,7 +98,6 @@ public: ContainerDeletePolicy::destroy (oldObject); } - /** Sets this pointer to a new object, deleting the old object that it was previously pointing to if there was one. */ void reset (ObjectType* newObject) { if (object != newObject) @@ -171,21 +114,14 @@ public: } } - /** Sets this pointer to a new object, deleting the old object that it was previously pointing to if there was one. */ void reset (ScopedPointer& newObject) { reset (newObject.release()); } - /** Detaches and returns the current object from this ScopedPointer without deleting it. - This will return the current object, and set the ScopedPointer to a null pointer. - */ - ObjectType* release() noexcept { auto* o = object; object = {}; return o; } + ObjectType* release() noexcept { auto* o = object; object = {}; return o; } //============================================================================== - /** Swaps this object with that of another ScopedPointer. - The two objects simply exchange their pointers. - */ void swapWith (ScopedPointer& other) noexcept { // Two ScopedPointers should never be able to refer to the same object - if @@ -195,10 +131,7 @@ public: std::swap (object, other.object); } - /** If the pointer is non-null, this will attempt to return a new copy of the object that is pointed to. - If the pointer is null, this will safely return a nullptr. - */ - inline ObjectType* createCopy() const { return createCopyIfNotNull (object); } + inline ObjectType* createCopy() const { return createCopyIfNotNull (object); } private: //============================================================================== @@ -213,70 +146,60 @@ private: }; //============================================================================== -/** Compares a ScopedPointer with another pointer. */ template bool operator== (ObjectType1* pointer1, const ScopedPointer& pointer2) noexcept { return pointer1 == pointer2.get(); } -/** Compares a ScopedPointer with another pointer. */ template bool operator!= (ObjectType1* pointer1, const ScopedPointer& pointer2) noexcept { return pointer1 != pointer2.get(); } -/** Compares a ScopedPointer with another pointer. */ template bool operator== (const ScopedPointer& pointer1, ObjectType2* pointer2) noexcept { return pointer1.get() == pointer2; } -/** Compares a ScopedPointer with another pointer. */ template bool operator!= (const ScopedPointer& pointer1, ObjectType2* pointer2) noexcept { return pointer1.get() != pointer2; } -/** Compares a ScopedPointer with another pointer. */ template bool operator== (const ScopedPointer& pointer1, const ScopedPointer& pointer2) noexcept { return pointer1.get() == pointer2.get(); } -/** Compares a ScopedPointer with another pointer. */ template bool operator!= (const ScopedPointer& pointer1, const ScopedPointer& pointer2) noexcept { return pointer1.get() != pointer2.get(); } -/** Compares a ScopedPointer with a nullptr. */ template bool operator== (decltype (nullptr), const ScopedPointer& pointer) noexcept { return pointer.get() == nullptr; } -/** Compares a ScopedPointer with a nullptr. */ template bool operator!= (decltype (nullptr), const ScopedPointer& pointer) noexcept { return pointer.get() != nullptr; } -/** Compares a ScopedPointer with a nullptr. */ template bool operator== (const ScopedPointer& pointer, decltype (nullptr)) noexcept { return pointer.get() == nullptr; } -/** Compares a ScopedPointer with a nullptr. */ template bool operator!= (const ScopedPointer& pointer, decltype (nullptr)) noexcept { @@ -284,11 +207,11 @@ bool operator!= (const ScopedPointer& pointer, decltype (nullptr)) n } //============================================================================== -#ifndef DOXYGEN // NB: This is just here to prevent any silly attempts to call deleteAndZero() on a ScopedPointer. template void deleteAndZero (ScopedPointer&) { static_assert (sizeof (Type) == 12345, "Attempt to call deleteAndZero() on a ScopedPointer"); } -#endif } // namespace juce + +#endif diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_WeakReference.h b/JuceLibraryCode/modules/juce_core/memory/juce_WeakReference.h index 2f6199e..85f4d21 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_WeakReference.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_WeakReference.h @@ -105,10 +105,7 @@ public: operator ObjectType*() const noexcept { return get(); } /** Returns the object that this pointer refers to, or null if the object no longer exists. */ - ObjectType* operator->() noexcept { return get(); } - - /** Returns the object that this pointer refers to, or null if the object no longer exists. */ - const ObjectType* operator->() const noexcept { return get(); } + ObjectType* operator->() const noexcept { return get(); } /** This returns true if this reference has been pointing at an object, but that object has since been deleted. diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_ConsoleApplication.cpp b/JuceLibraryCode/modules/juce_core/misc/juce_ConsoleApplication.cpp index 0a1cfd0..da5548b 100644 --- a/JuceLibraryCode/modules/juce_core/misc/juce_ConsoleApplication.cpp +++ b/JuceLibraryCode/modules/juce_core/misc/juce_ConsoleApplication.cpp @@ -28,16 +28,31 @@ static inline File resolveFilename (const String& name) return File::getCurrentWorkingDirectory().getChildFile (name.unquoted()); } -static inline void checkFileExists (const File& f) +static inline File checkFileExists (const File& f) { if (! f.exists()) ConsoleApplication::fail ("Could not find file: " + f.getFullPathName()); + + return f; } -static inline void checkFolderExists (const File& f) +static inline File checkFolderExists (const File& f) { if (! f.isDirectory()) ConsoleApplication::fail ("Could not find folder: " + f.getFullPathName()); + + return f; +} + +static inline File resolveFilenameForOption (const ArgumentList& args, StringRef option, const String& filename) +{ + if (filename.isEmpty()) + { + args.failIfOptionIsMissing (option); + ConsoleApplication::fail ("Expected a filename after the " + option + " option"); + } + + return resolveFilename (filename); } File ArgumentList::Argument::resolveAsFile() const @@ -47,9 +62,7 @@ File ArgumentList::Argument::resolveAsFile() const File ArgumentList::Argument::resolveAsExistingFile() const { - auto f = resolveAsFile(); - checkFileExists (f); - return f; + return checkFileExists (resolveAsFile()); } File ArgumentList::Argument::resolveAsExistingFolder() const @@ -84,8 +97,12 @@ bool ArgumentList::Argument::isLongOption (const String& option) const String ArgumentList::Argument::getLongOptionValue() const { if (isLongOption()) - if (auto equalsIndex = text.indexOfChar ('=')) + { + auto equalsIndex = text.indexOfChar ('='); + + if (equalsIndex > 0) return text.substring (equalsIndex + 1); + } return {}; } @@ -94,7 +111,7 @@ bool ArgumentList::Argument::isShortOption (char option) const { jassert (option != '-'); // this is probably not what you intended to pass in - return isShortOption() && text.containsChar (option); + return isShortOption() && text.containsChar (String (option)[0]); } bool ArgumentList::Argument::operator== (StringRef wildcard) const @@ -162,9 +179,19 @@ bool ArgumentList::containsOption (StringRef option) const return indexOfOption (option) >= 0; } +bool ArgumentList::removeOptionIfFound (StringRef option) +{ + auto i = indexOfOption (option); + + if (i >= 0) + arguments.remove (i); + + return i >= 0; +} + void ArgumentList::failIfOptionIsMissing (StringRef option) const { - if (! containsOption (option)) + if (indexOfOption (option) < 0) ConsoleApplication::fail ("Expected the option " + option); } @@ -194,31 +221,69 @@ String ArgumentList::getValueForOption (StringRef option) const return {}; } -File ArgumentList::getFileForOption (StringRef option) const +String ArgumentList::removeValueForOption (StringRef option) { - auto text = getValueForOption (option); + jassert (isOptionFormat (option)); // the thing you're searching for must be an option - if (text.isEmpty()) + for (int i = 0; i < arguments.size(); ++i) { - failIfOptionIsMissing (option); - ConsoleApplication::fail ("Expected a filename after the " + option + " option"); + auto& arg = arguments.getReference(i); + + if (arg == option) + { + if (arg.isShortOption()) + { + if (i < arguments.size() - 1 && ! arguments.getReference (i + 1).isOption()) + { + auto result = arguments.getReference (i + 1).text; + arguments.removeRange (i, 2); + return result; + } + + arguments.remove (i); + return {}; + } + + if (arg.isLongOption()) + { + auto result = arg.getLongOptionValue(); + arguments.remove (i); + return result; + } + } } - return resolveFilename (text); + return {}; +} + +File ArgumentList::getFileForOption (StringRef option) const +{ + return resolveFilenameForOption (*this, option, getValueForOption (option)); +} + +File ArgumentList::getFileForOptionAndRemove (StringRef option) +{ + return resolveFilenameForOption (*this, option, removeValueForOption (option)); } File ArgumentList::getExistingFileForOption (StringRef option) const { - auto file = getFileForOption (option); - checkFileExists (file); - return file; + return checkFileExists (getFileForOption (option)); +} + +File ArgumentList::getExistingFileForOptionAndRemove (StringRef option) +{ + return checkFileExists (getFileForOptionAndRemove (option)); } File ArgumentList::getExistingFolderForOption (StringRef option) const { - auto file = getFileForOption (option); - checkFolderExists (file); - return file; + return checkFolderExists (getFileForOption (option)); +} + +File ArgumentList::getExistingFolderForOptionAndRemove (StringRef option) +{ + return checkFolderExists (getFileForOptionAndRemove (option)); } //============================================================================== @@ -243,7 +308,7 @@ int ConsoleApplication::invokeCatchingFailures (std::function&& f) } catch (const ConsoleAppFailureCode& error) { - std::cout << error.errorMessage << std::endl; + std::cerr << error.errorMessage << std::endl; returnCode = error.returnCode; } @@ -268,11 +333,15 @@ const ConsoleApplication::Command* ConsoleApplication::findCommand (const Argume int ConsoleApplication::findAndRunCommand (const ArgumentList& args, bool optionMustBeFirstArg) const { - if (auto c = findCommand (args, optionMustBeFirstArg)) - return invokeCatchingFailures ([=] { c->command (args); return 0; }); + return invokeCatchingFailures ([&args, optionMustBeFirstArg, this] + { + if (auto c = findCommand (args, optionMustBeFirstArg)) + c->command (args); + else + fail ("Unrecognised arguments"); - fail ("Unrecognised arguments"); - return 0; + return 0; + }); } int ConsoleApplication::findAndRunCommand (int argc, char* argv[]) const @@ -320,37 +389,51 @@ const std::vector& ConsoleApplication::getCommands( return commands; } -void ConsoleApplication::printCommandList (const ArgumentList& args) const +static String getExeNameAndArgs (const ArgumentList& args, const ConsoleApplication::Command& command) { auto exeName = args.executableName.fromLastOccurrenceOf ("/", false, false) .fromLastOccurrenceOf ("\\", false, false); - StringArray namesAndArgs; + return " " + exeName + " " + command.argumentDescription; +} + +static void printCommandDescription (const ArgumentList& args, const ConsoleApplication::Command& command, + int descriptionIndent) +{ + auto nameAndArgs = getExeNameAndArgs (args, command); + + if (nameAndArgs.length() > descriptionIndent) + std::cout << nameAndArgs << std::endl << String().paddedRight (' ', descriptionIndent); + else + std::cout << nameAndArgs.paddedRight (' ', descriptionIndent); + + std::cout << command.shortDescription << std::endl; +} + +void ConsoleApplication::printCommandList (const ArgumentList& args) const +{ int descriptionIndent = 0; for (auto& c : commands) - { - auto nameAndArgs = exeName + " " + c.argumentDescription; - namesAndArgs.add (nameAndArgs); - descriptionIndent = std::max (descriptionIndent, nameAndArgs.length()); - } + descriptionIndent = std::max (descriptionIndent, getExeNameAndArgs (args, c).length()); - descriptionIndent = std::min (descriptionIndent + 1, 40); + descriptionIndent = std::min (descriptionIndent + 2, 40); - for (size_t i = 0; i < commands.size(); ++i) - { - auto nameAndArgs = namesAndArgs[(int) i]; - std::cout << ' '; - - if (nameAndArgs.length() > descriptionIndent) - std::cout << nameAndArgs << std::endl << String::repeatedString (" ", descriptionIndent + 1); - else - std::cout << nameAndArgs.paddedRight (' ', descriptionIndent); - - std::cout << commands[i].shortDescription << std::endl; - } + for (auto& c : commands) + printCommandDescription (args, c, descriptionIndent); std::cout << std::endl; } +void ConsoleApplication::printCommandDetails (const ArgumentList& args, const Command& command) const +{ + auto len = getExeNameAndArgs (args, command).length(); + + printCommandDescription (args, command, std::min (len + 3, 40)); + + if (command.longDescription.isNotEmpty()) + std::cout << std::endl << command.longDescription << std::endl; +} + + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_ConsoleApplication.h b/JuceLibraryCode/modules/juce_core/misc/juce_ConsoleApplication.h index 336bb9d..a644017 100644 --- a/JuceLibraryCode/modules/juce_core/misc/juce_ConsoleApplication.h +++ b/JuceLibraryCode/modules/juce_core/misc/juce_ConsoleApplication.h @@ -32,6 +32,8 @@ namespace juce main() function to parse. @see ConsoleApplication + + @tags{Core} */ struct ArgumentList { @@ -50,6 +52,8 @@ struct ArgumentList //============================================================================== /** One of the arguments in an ArgumentList. + + @tags{Core} */ struct Argument { @@ -120,9 +124,17 @@ struct ArgumentList /** Returns true if the given string matches one of the arguments. The option can also be a list of different versions separated by pipes, e.g. "--help|-h" + @see removeOptionIfFound */ bool containsOption (StringRef option) const; + /** Returns true if the given string matches one of the arguments, and also removes the + argument from the list if found. + The option can also be a list of different versions separated by pipes, e.g. "--help|-h" + @see containsOption + */ + bool removeOptionIfFound (StringRef option); + /** Returns the index of the given string if it matches one of the arguments, or -1 if it doesn't. The option can also be a list of different versions separated by pipes, e.g. "--help|-h" */ @@ -142,23 +154,48 @@ struct ArgumentList */ String getValueForOption (StringRef option) const; - /** Looks for the value of argument using getValueForOption() and tries to parse that value - as a file. - If the option isn't found, or if the value can't be parsed as a filename, it will throw - an error. + /** Looks for a given argument and returns either its assigned value (for long options) or the + string that follows it (for short options). + This works like getValueForOption() but also removes the option argument (and any value arguments) + from the list if they are found. + */ + String removeValueForOption (StringRef option); + + /** Looks for the value of argument using getValueForOption() and tries to parse that value as a file. + If the option isn't found, or if the value can't be parsed as a filename, it will throw an error. */ File getFileForOption (StringRef option) const; + /** Looks for the value of argument using getValueForOption() and tries to parse that value as a file. + This works like getFileForOption() but also removes the option argument (and any value arguments) + from the list if they are found. + */ + File getFileForOptionAndRemove (StringRef option); + /** Looks for a file argument using getFileForOption() and fails with a suitable error if the file doesn't exist. */ File getExistingFileForOption (StringRef option) const; + /** Looks for a file argument using getFileForOption() and fails with a suitable error if + the file doesn't exist. + This works like getExistingFileForOption() but also removes the option argument (and any + value arguments) from the list if they are found. + */ + File getExistingFileForOptionAndRemove (StringRef option); + /** Looks for a filename argument using getFileForOption() and fails with a suitable error if the file isn't a folder that exists. */ File getExistingFolderForOption (StringRef option) const; + /** Looks for a filename argument using getFileForOption() and fails with a suitable error if + the file isn't a folder that exists. + This works like getExistingFolderForOption() but also removes the option argument (and any + value arguments) from the list if they are found. + */ + File getExistingFolderForOptionAndRemove (StringRef option); + /** The name or path of the executable that was invoked, as it was specified on the command-line. */ String executableName; @@ -193,13 +230,18 @@ struct ArgumentList @endcode @see ArgumentList + + @tags{Core} */ struct ConsoleApplication { //============================================================================== /** Represents a command that can be executed if its command-line arguments are matched. + @see ConsoleApplication::addCommand(), ConsoleApplication::findAndRunCommand() + + @tags{Core} */ struct Command { @@ -248,6 +290,11 @@ struct ConsoleApplication */ void printCommandList (const ArgumentList&) const; + /** Prints out a longer description of a particular command, based on its + longDescription member. + */ + void printCommandDetails (const ArgumentList&, const Command&) const; + //============================================================================== /** Throws a failure exception to cause a command-line app to terminate. This is intended to be called from code in a Command, so that the diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_RuntimePermissions.h b/JuceLibraryCode/modules/juce_core/misc/juce_RuntimePermissions.h index 25d87fc..7cd9794 100644 --- a/JuceLibraryCode/modules/juce_core/misc/juce_RuntimePermissions.h +++ b/JuceLibraryCode/modules/juce_core/misc/juce_RuntimePermissions.h @@ -91,7 +91,7 @@ public: //============================================================================== /** Function type of runtime permission request callbacks. */ - using Callback = std::function; + using Callback = std::function; //============================================================================== /** Call this method to request a runtime permission. diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_StdFunctionCompat.cpp b/JuceLibraryCode/modules/juce_core/misc/juce_StdFunctionCompat.cpp deleted file mode 100644 index f6139e3..0000000 --- a/JuceLibraryCode/modules/juce_core/misc/juce_StdFunctionCompat.cpp +++ /dev/null @@ -1,282 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - Permission is granted to use this software under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license/ - - 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 ISC DISCLAIMS ALL WARRANTIES WITH REGARD - TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS. IN NO EVENT SHALL ISC 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. - - ----------------------------------------------------------------------------- - - To release a closed-source product which uses other parts of JUCE not - licensed under the ISC terms, commercial licenses are available: visit - www.juce.com for more information. - - ============================================================================== -*/ - -namespace juce -{ - -#if JUCE_UNIT_TESTS - -namespace FunctionTestsHelpers -{ - static void incrementArgument (int& x) { x++; } - static double multiply (double x, double a) noexcept { return a * x; } - - struct BigData - { - BigData() - { - for (auto i = 0; i < bigDataSize; ++i) - content[i] = i + 1; - } - - int sum() const - { - int result = 0; - for (auto i = 0; i < bigDataSize; ++i) - result += content[i]; - - return result; - } - - static const int bigDataSize = 32, - bigDataSum = bigDataSize * (bigDataSize + 1) / 2; - int content[bigDataSize]; - }; - - struct FunctionObject - { - FunctionObject() = default; - - FunctionObject (const FunctionObject& other) - { - bigData.reset (new BigData (*other.bigData)); - } - - int operator()(int i) const { return bigData->sum() + i; } - - std::unique_ptr bigData { new BigData() }; - - JUCE_LEAK_DETECTOR (FunctionObject) - }; - - struct BigFunctionObject - { - BigFunctionObject() = default; - - BigFunctionObject (const BigFunctionObject& other) - { - bigData.reset (new BigData (*other.bigData)); - } - - int operator()(int i) const { return bigData->sum() + i; } - - std::unique_ptr bigData { new BigData() }; - - int stackUsage[32]; - - JUCE_LEAK_DETECTOR (BigFunctionObject) - }; -} - -class FunctionTests : public UnitTest -{ -public: - FunctionTests() : UnitTest ("Function", "Function") {} - - void runTest() override - { - FunctionTestsHelpers::BigData bigData; - - { - beginTest ("Functions"); - - std::function f1 (FunctionTestsHelpers::incrementArgument); - - auto x = 0; - f1 (x); - expectEquals (x, 1); - - std::function f2 (FunctionTestsHelpers::multiply); - expectEquals (6.0, f2 (2.0, 3.0)); - } - - { - beginTest ("Function objects"); - - std::function f1 = FunctionTestsHelpers::FunctionObject(); - expectEquals (f1 (5), FunctionTestsHelpers::BigData::bigDataSum + 5); - - std::function f2 { FunctionTestsHelpers::BigFunctionObject() }; - expectEquals (f2 (5), FunctionTestsHelpers::BigData::bigDataSum + 5); - } - - { - beginTest ("Lambdas"); - - std::function fStack ([] { return 3; }); - expectEquals (fStack(), 3); - - std::function fHeap ([=] { return bigData.sum(); }); - expectEquals (fHeap(), FunctionTestsHelpers::BigData::bigDataSum); - } - - { - beginTest ("Boolean"); - - std::function f1; - - if (f1) - expect (false); - - std::function f2 ([]() { return 3; }); - - if (! f2) - expect (false); - } - - std::function fEmpty; - - std::function fStack ([] { return 3; }); - - std::function fHeap ([=] { return bigData.sum(); }); - - { - beginTest ("copy constructor"); - - std::function f1 (fStack); - expectEquals (f1(), 3); - - std::function f2 (fHeap); - expectEquals (f2(), FunctionTestsHelpers::BigData::bigDataSum); - - std::function f3 (fEmpty); - if (f3) - expect (false); - } - - { - beginTest ("assignment"); - - std::function f1; - f1 = fStack; - expectEquals (f1(), 3); - - std::function f2; - f2 = fHeap; - expectEquals (f2(), FunctionTestsHelpers::BigData::bigDataSum); - - f1 = fHeap; - expectEquals (f1(), FunctionTestsHelpers::BigData::bigDataSum); - - f2 = fStack; - expectEquals (f2(), 3); - - f1 = fEmpty; - if (f1) - expect (false); - } - - { - beginTest ("move constructor"); - - std::unique_ptr> fStackTmp (new std::function (fStack)); - std::function f1 (std::move (*fStackTmp)); - - fStackTmp.reset(); - expectEquals (f1(), 3); - - std::unique_ptr> fHeapTmp (new std::function (fHeap)); - std::function f2 (std::move (*fHeapTmp)); - if (*fHeapTmp) - expect (false); - - fHeapTmp.reset(); - expectEquals (f2(), FunctionTestsHelpers::BigData::bigDataSum); - - std::unique_ptr> fEmptyTmp (new std::function()); - std::function f3 (std::move (*fEmptyTmp)); - fEmptyTmp.reset(); - if (f3) - expect (false); - } - - { - beginTest ("move assignment"); - - std::function f1 (fHeap); - std::unique_ptr> fStackTmp (new std::function (fStack)); - f1 = std::move (*fStackTmp); - - fStackTmp.reset(); - expectEquals (f1(), 3); - - std::function f2 (fStack); - std::unique_ptr> fHeapTmp (new std::function (fHeap)); - f2 = std::move (*fHeapTmp); - if (*fHeapTmp) - expect (false); - - fHeapTmp.reset(); - expectEquals (f2(), FunctionTestsHelpers::BigData::bigDataSum); - - std::function f3 (fHeap); - std::unique_ptr> fEmptyTmp (new std::function()); - f3 = std::move (*fEmptyTmp); - fEmptyTmp.reset(); - if (f3) - expect (false); - } - - { - beginTest ("nullptr"); - - std::function f1 (nullptr); - if (f1) - expect (false); - - std::function f2 ([]() { return 11; }); - f2 = nullptr; - if (f2) - expect (false); - } - - { - beginTest ("Swap"); - - std::function f1; - std::function f2 (fStack); - f2.swap (f1); - expectEquals (f1(), 3); - if (f2) - expect (false); - - std::function f3 (fHeap); - f3.swap (f1); - expectEquals (f3(), 3); - expectEquals (f1(), FunctionTestsHelpers::BigData::bigDataSum); - } - } -}; - -static FunctionTests functionTests; - -#endif - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_StdFunctionCompat.h b/JuceLibraryCode/modules/juce_core/misc/juce_StdFunctionCompat.h deleted file mode 100644 index 5406cd9..0000000 --- a/JuceLibraryCode/modules/juce_core/misc/juce_StdFunctionCompat.h +++ /dev/null @@ -1,206 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - Permission is granted to use this software under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license/ - - 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 ISC DISCLAIMS ALL WARRANTIES WITH REGARD - TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS. IN NO EVENT SHALL ISC 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. - - ----------------------------------------------------------------------------- - - To release a closed-source product which uses other parts of JUCE not - licensed under the ISC terms, commercial licenses are available: visit - www.juce.com for more information. - - ============================================================================== -*/ - -namespace std -{ - /** - This class provides an alternative to std::function that is compatible - with OS X 10.6 and earlier. This will only be used in OS X versions 10.6 - and earlier and the Projucer live build. - - @tags{Core} - */ - template - class function; - - #ifndef DOXYGEN - template - class function - { - public: - /** Creates an empty function. */ - function() noexcept {} - - /** Creates an empty function. */ - function (decltype (nullptr)) noexcept {} - - /** Creates a function targeting the provided Functor. */ - template - function (Functor f) - { - functorHolderHelper = getFunctorStorage (sizeof (FunctorHolder)); - new (functorHolderHelper) FunctorHolder (f); - } - - /** Copy constructor. */ - function (function const& other) - { - copy (other); - } - - /** Move constructor */ - function (function&& other) - { - move (other); - } - - /** Destructor. */ - ~function() - { - release(); - } - - /** Replaces the contents of this function with the contents of another. */ - function& operator= (function const& other) - { - release(); - copy (other); - - return *this; - } - - /** Moves the contents of another function into this one. */ - function& operator= (function&& other) - { - release(); - move (other); - - return *this; - } - - /** Allows conditional expressions to test if this function is empty. */ - explicit operator bool() const noexcept - { - return functorHolderHelper != nullptr; - } - - /** Swaps the contents of this function with another. After this operation the - two functions will be pointing at each other's targets. */ - void swap (function& other) - { - function tmp (*this); - *this = other; - other = tmp; - } - - /** Invokes the target of this function. */ - Result operator() (Arguments... args) const - { - return (*functorHolderHelper) (args...); - } - - bool operator== (decltype (nullptr)) const noexcept { return (functorHolderHelper == nullptr); } - bool operator!= (decltype (nullptr)) const noexcept { return (functorHolderHelper != nullptr); } - - private: - //============================================================================== - template - struct FunctorHolderBase - { - virtual ~FunctorHolderBase() {} - virtual int getSize() const noexcept = 0; - virtual void copy (void*) const = 0; - virtual ReturnType operator()(Args...) = 0; - }; - - template - struct FunctorHolder : FunctorHolderBase - { - FunctorHolder (Functor func) : f (func) {} - - int getSize() const noexcept override final - { - return sizeof (*this); - } - - void copy (void* destination) const override final - { - new (destination) FunctorHolder (f); - } - - ReturnType operator()(Args... args) override final - { - return f (args...); - } - - Functor f; - }; - - FunctorHolderBase* getFunctorStorage (int size) - { - return reinterpret_cast*> - (size > functorHolderStackSize ? new char [static_cast (size)] - : &(stackFunctorStorage[0])); - } - - void copy (function const& other) - { - if (other.functorHolderHelper != nullptr) - { - functorHolderHelper = getFunctorStorage (other.functorHolderHelper->getSize()); - other.functorHolderHelper->copy (functorHolderHelper); - } - } - - void move (function& other) - { - if (other.functorHolderHelper != nullptr) - { - if (other.functorHolderHelper->getSize() > functorHolderStackSize) - { - functorHolderHelper = other.functorHolderHelper; - } - else - { - std::copy (other.stackFunctorStorage, other.stackFunctorStorage + functorHolderStackSize, - stackFunctorStorage); - functorHolderHelper = reinterpret_cast*> (&(stackFunctorStorage[0])); - } - - other.functorHolderHelper = nullptr; - } - } - - void release() - { - if (functorHolderHelper != nullptr) - { - functorHolderHelper->~FunctorHolderBase(); - functorHolderHelper = nullptr; - } - } - - static const int functorHolderStackSize = 24; - char stackFunctorStorage[functorHolderStackSize]; - - FunctorHolderBase* functorHolderHelper = nullptr; - }; - #endif -} diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_WindowsRegistry.h b/JuceLibraryCode/modules/juce_core/misc/juce_WindowsRegistry.h index e1d0761..be61c20 100644 --- a/JuceLibraryCode/modules/juce_core/misc/juce_WindowsRegistry.h +++ b/JuceLibraryCode/modules/juce_core/misc/juce_WindowsRegistry.h @@ -90,7 +90,7 @@ public: static bool JUCE_CALLTYPE valueExists (const String& regValuePath, WoW64Mode mode = WoW64_Default); /** Returns true if the given key exists in the registry. */ - static bool JUCE_CALLTYPE keyExists (const String& regValuePath, WoW64Mode mode = WoW64_Default); + static bool JUCE_CALLTYPE keyExists (const String& regKeyPath, WoW64Mode mode = WoW64_Default); /** Deletes a registry value. */ static bool JUCE_CALLTYPE deleteValue (const String& regValuePath, WoW64Mode mode = WoW64_Default); diff --git a/JuceLibraryCode/modules/juce_core/native/java/README.txt b/JuceLibraryCode/modules/juce_core/native/java/README.txt index 612d87c..d7cf93c 100644 --- a/JuceLibraryCode/modules/juce_core/native/java/README.txt +++ b/JuceLibraryCode/modules/juce_core/native/java/README.txt @@ -26,8 +26,8 @@ Remove any classes that you are not interested in (typically you'll find Java.class, JuceApp.class and JuceSharingContentProvider.class which you will probably want to remove). -6. Inside of app/build/intermediates/classes/release_/release execute the -following dx command: +6. Inside of build/intermediates/javac/release_Release/compileRelease_ReleaseJavaWithJavac/classes +execute the following dx command: /build-tools//dx --dex --verbose --min-sdk-version= --output /tmp/JavaDexByteCode.dex . diff --git a/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h b/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h index 7ff4e62..9ec827f 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h +++ b/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h @@ -28,12 +28,20 @@ #if JUCE_MAC || JUCE_IOS #if JUCE_IOS + #if JUCE_MODULE_AVAILABLE_juce_opengl && defined (__IPHONE_12_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_12_0 + #define GLES_SILENCE_DEPRECATION 1 + #endif + #import #import #import #import #include #else + #if JUCE_MODULE_AVAILABLE_juce_opengl && defined (MAC_OS_X_VERSION_10_14) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 + #define GL_SILENCE_DEPRECATION 1 + #endif + #import #if (! defined MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 #define NSEventModifierFlagCommand NSCommandKeyMask @@ -96,6 +104,7 @@ #include #include #include + #include //============================================================================== #elif JUCE_WINDOWS @@ -167,7 +176,7 @@ #pragma warning (4: 4511 4512 4100) #endif - #if JUCE_MSVC && ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES + #if ! JUCE_MINGW && ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES #pragma comment (lib, "kernel32.lib") #pragma comment (lib, "user32.lib") #pragma comment (lib, "wininet.lib") @@ -233,6 +242,7 @@ #include #include #include + #include //============================================================================== #elif JUCE_BSD @@ -261,6 +271,7 @@ #include #include #include + #include //============================================================================== #elif JUCE_ANDROID @@ -282,6 +293,7 @@ #include #include #include + #include // If you are getting include errors here, then you to re-build the Projucer // and re-save your .jucer file. diff --git a/JuceLibraryCode/modules/juce_core/native/juce_android_Files.cpp b/JuceLibraryCode/modules/juce_core/native/juce_android_Files.cpp index 115dba7..4163c59 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_android_Files.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_android_Files.cpp @@ -89,7 +89,7 @@ static File getWellKnownFolder (const char* folderId) auto* env = getEnv(); auto fieldId = env->GetStaticFieldID (AndroidEnvironment, folderId, "Ljava/lang/String;"); - if (fieldId == 0) + if (fieldId == nullptr) { // unknown field in environment jassertfalse; @@ -194,7 +194,7 @@ public: auto* env = getEnv(); LocalRef contentResolver (env->CallObjectMethod (getAppContext().get(), AndroidContext.getContentResolver)); - if (contentResolver == 0) + if (contentResolver == nullptr) return {}; auto filename = getStringUsingDataColumn ("_display_name", env, uri, contentResolver); @@ -298,7 +298,7 @@ private: auto* env = getEnv(); static jmethodID m = (env->GetMethodID (AndroidContext, "getExternalFilesDirs", "(Ljava/lang/String;)[Ljava/io/File;")); - if (m == 0) + if (m == nullptr) return {}; auto paths = convertFileArray (LocalRef (env->CallObjectMethod (getAppContext().get(), m, nullptr))); @@ -406,7 +406,7 @@ private: return {}; } - if (cursor == 0) + if (cursor == nullptr) return {}; String fileName; @@ -439,7 +439,7 @@ struct AndroidContentUriOutputStream : public OutputStream { } - ~AndroidContentUriOutputStream() + ~AndroidContentUriOutputStream() override { stream.callVoidMethod (AndroidOutputStream.close); } @@ -484,7 +484,7 @@ OutputStream* juce_CreateContentURIOutputStream (const URL& url) { auto stream = AndroidContentUriResolver::getStreamForContentUri (url, false); - return (stream.get() != 0 ? new AndroidContentUriOutputStream (std::move (stream)) : nullptr); + return (stream.get() != nullptr ? new AndroidContentUriOutputStream (std::move (stream)) : nullptr); } //============================================================================== @@ -676,7 +676,7 @@ private: void FileOutputStream::flushInternal() { - if (fileHandle != 0) + if (fileHandle != nullptr) { if (fsync (getFD (fileHandle)) == -1) status = getResultForErrno(); diff --git a/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.cpp b/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.cpp index cda9a67..d9eb263 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.cpp @@ -116,7 +116,7 @@ struct SystemJavaClassComparator //============================================================================== JNIClassBase::JNIClassBase (const char* cp, int classMinSDK, const void* bc, size_t n) - : classPath (cp), byteCode (bc), byteCodeSize (n), minSDK (classMinSDK), classRef (0) + : classPath (cp), byteCode (bc), byteCodeSize (n), minSDK (classMinSDK), classRef (nullptr) { SystemJavaClassComparator comparator; @@ -162,13 +162,13 @@ void JNIClassBase::initialise (JNIEnv* env) LocalRef defaultClassLoader (env->CallStaticObjectMethod (JavaClassLoader, JavaClassLoader.getSystemClassLoader)); tryLoadingClassWithClassLoader (env, defaultClassLoader.get()); - if (classRef == 0) + if (classRef == nullptr) { for (auto& byteCodeLoader : byteCodeLoaders) { tryLoadingClassWithClassLoader (env, byteCodeLoader.get()); - if (classRef != 0) + if (classRef != nullptr) break; } @@ -234,10 +234,10 @@ void JNIClassBase::initialise (JNIEnv* env) } } - if (classRef == 0) + if (classRef == nullptr) classRef = (jclass) env->NewGlobalRef (LocalRef (env->FindClass (classPath))); - jassert (classRef != 0); + jassert (classRef != nullptr); initialiseFields (env); } } @@ -253,7 +253,7 @@ void JNIClassBase::tryLoadingClassWithClassLoader (JNIEnv* env, jobject classLoa if (jthrowable exception = env->ExceptionOccurred ()) { env->ExceptionClear(); - classObj = 0; + classObj = nullptr; } // later versions of Android don't throw at all, so re-check the object @@ -263,7 +263,7 @@ void JNIClassBase::tryLoadingClassWithClassLoader (JNIEnv* env, jobject classLoa void JNIClassBase::release (JNIEnv* env) { - if (classRef != 0) + if (classRef != nullptr) env->DeleteGlobalRef (classRef); } @@ -284,28 +284,28 @@ void JNIClassBase::releaseAllClasses (JNIEnv* env) jmethodID JNIClassBase::resolveMethod (JNIEnv* env, const char* methodName, const char* params) { jmethodID m = env->GetMethodID (classRef, methodName, params); - jassert (m != 0); + jassert (m != nullptr); return m; } jmethodID JNIClassBase::resolveStaticMethod (JNIEnv* env, const char* methodName, const char* params) { jmethodID m = env->GetStaticMethodID (classRef, methodName, params); - jassert (m != 0); + jassert (m != nullptr); return m; } jfieldID JNIClassBase::resolveField (JNIEnv* env, const char* fieldName, const char* signature) { jfieldID f = env->GetFieldID (classRef, fieldName, signature); - jassert (f != 0); + jassert (f != nullptr); return f; } jfieldID JNIClassBase::resolveStaticField (JNIEnv* env, const char* fieldName, const char* signature) { jfieldID f = env->GetStaticFieldID (classRef, fieldName, signature); - jassert (f != 0); + jassert (f != nullptr); return f; } @@ -418,13 +418,27 @@ jobject ActivityLifecycleCallbacks::invoke (jobject proxy, jobject method, jobje auto activity = env->GetArrayLength (args) > 0 ? env->GetObjectArrayElement (args, 0) : (jobject) nullptr; auto bundle = env->GetArrayLength (args) > 1 ? env->GetObjectArrayElement (args, 1) : (jobject) nullptr; - if (methodName == "onActivityCreated") { onActivityCreated (activity, bundle); return nullptr; } - else if (methodName == "onActivityDestroyed") { onActivityDestroyed (activity); return nullptr; } - else if (methodName == "onActivityPaused") { onActivityPaused (activity); return nullptr; } - else if (methodName == "onActivityResumed") { onActivityResumed (activity); return nullptr; } - else if (methodName == "onActivitySaveInstanceState") { onActivitySaveInstanceState (activity, bundle); return nullptr; } - else if (methodName == "onActivityStarted") { onActivityStarted (activity); return nullptr; } - else if (methodName == "onActivityStopped") { onActivityStopped (activity); return nullptr; } + if (methodName == "onActivityPreCreated") { onActivityPreCreated (activity, bundle); return nullptr; } + else if (methodName == "onActivityPreDestroyed") { onActivityPreDestroyed (activity); return nullptr; } + else if (methodName == "onActivityPrePaused") { onActivityPrePaused (activity); return nullptr; } + else if (methodName == "onActivityPreResumed") { onActivityPreResumed (activity); return nullptr; } + else if (methodName == "onActivityPreSaveInstanceState") { onActivityPreSaveInstanceState (activity, bundle); return nullptr; } + else if (methodName == "onActivityPreStarted") { onActivityPreStarted (activity); return nullptr; } + else if (methodName == "onActivityPreStopped") { onActivityPreStopped (activity); return nullptr; } + else if (methodName == "onActivityCreated") { onActivityCreated (activity, bundle); return nullptr; } + else if (methodName == "onActivityDestroyed") { onActivityDestroyed (activity); return nullptr; } + else if (methodName == "onActivityPaused") { onActivityPaused (activity); return nullptr; } + else if (methodName == "onActivityResumed") { onActivityResumed (activity); return nullptr; } + else if (methodName == "onActivitySaveInstanceState") { onActivitySaveInstanceState (activity, bundle); return nullptr; } + else if (methodName == "onActivityStarted") { onActivityStarted (activity); return nullptr; } + else if (methodName == "onActivityStopped") { onActivityStopped (activity); return nullptr; } + else if (methodName == "onActivityPostCreated") { onActivityPostCreated (activity, bundle); return nullptr; } + else if (methodName == "onActivityPostDestroyed") { onActivityPostDestroyed (activity); return nullptr; } + else if (methodName == "onActivityPostPaused") { onActivityPostPaused (activity); return nullptr; } + else if (methodName == "onActivityPostResumed") { onActivityPostResumed (activity); return nullptr; } + else if (methodName == "onActivityPostSaveInstanceState") { onActivityPostSaveInstanceState (activity, bundle); return nullptr; } + else if (methodName == "onActivityPostStarted") { onActivityPostStarted (activity); return nullptr; } + else if (methodName == "onActivityPostStopped") { onActivityPostStopped (activity); return nullptr; } return AndroidInterfaceImplementer::invoke (proxy, method, args); } @@ -440,10 +454,10 @@ int getAndroidSDKVersion() auto* env = getEnv(); auto buildVersion = env->FindClass ("android/os/Build$VERSION"); - jassert (buildVersion != 0); + jassert (buildVersion != nullptr); auto sdkVersionField = env->GetStaticFieldID (buildVersion, "SDK_INT", "I"); - jassert (sdkVersionField != 0); + jassert (sdkVersionField != nullptr); return env->GetStaticIntField (buildVersion, sdkVersionField); }(); @@ -581,7 +595,7 @@ void FragmentOverlay::onRequestPermissionsResultNative (JNIEnv* env, jobject, jl if (n > 0) { - auto* data = env->GetIntArrayElements (jGrantResults, 0); + auto* data = env->GetIntArrayElements (jGrantResults, nullptr); for (int i = 0; i < n; ++i) grantResults.add (data[i]); @@ -606,7 +620,7 @@ class ActivityLauncher : public FragmentOverlay public: ActivityLauncher (const LocalRef& intentToUse, int requestCodeToUse, - std::function)> && callbackToUse) + std::function)> && callbackToUse) : intent (intentToUse), requestCode (requestCodeToUse), callback (std::move (callbackToUse)) {} @@ -628,11 +642,11 @@ public: private: GlobalRef intent; int requestCode; - std::function)> callback; + std::function)> callback; }; void startAndroidActivityForResult (const LocalRef& intent, int requestCode, - std::function)> && callback) + std::function)> && callback) { auto* activityLauncher = new ActivityLauncher (intent, requestCode, std::move (callback)); activityLauncher->open(); diff --git a/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h b/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h index 8b6a8d9..edf7fad 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h +++ b/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h @@ -31,18 +31,18 @@ template class LocalRef { public: - explicit inline LocalRef() noexcept : obj (0) {} + explicit inline LocalRef() noexcept : obj (nullptr) {} explicit inline LocalRef (JavaType o) noexcept : obj (o) {} inline LocalRef (const LocalRef& other) noexcept : obj (retain (other.obj)) {} - inline LocalRef (LocalRef&& other) noexcept : obj (0) { std::swap (obj, other.obj); } + inline LocalRef (LocalRef&& other) noexcept : obj (nullptr) { std::swap (obj, other.obj); } ~LocalRef() { clear(); } void clear() { - if (obj != 0) + if (obj != nullptr) { getEnv()->DeleteLocalRef (obj); - obj = 0; + obj = nullptr; } } @@ -69,7 +69,7 @@ private: static JavaType retain (JavaType obj) { - return obj == 0 ? 0 : (JavaType) getEnv()->NewLocalRef (obj); + return obj == nullptr ? nullptr : (JavaType) getEnv()->NewLocalRef (obj); } }; @@ -77,21 +77,21 @@ private: class GlobalRef { public: - inline GlobalRef() noexcept : obj (0) {} + inline GlobalRef() noexcept : obj (nullptr) {} inline explicit GlobalRef (const LocalRef& o) : obj (retain (o.get(), getEnv())) {} inline explicit GlobalRef (const LocalRef& o, JNIEnv* env) : obj (retain (o.get(), env)) {} inline GlobalRef (const GlobalRef& other) : obj (retain (other.obj, getEnv())) {} - inline GlobalRef (GlobalRef && other) noexcept : obj (0) { std::swap (other.obj, obj); } + inline GlobalRef (GlobalRef && other) noexcept : obj (nullptr) { std::swap (other.obj, obj); } ~GlobalRef() { clear(); } - inline void clear() { if (obj != 0) clear (getEnv()); } + inline void clear() { if (obj != nullptr) clear (getEnv()); } inline void clear (JNIEnv* env) { - if (obj != 0) + if (obj != nullptr) { env->DeleteGlobalRef (obj); - obj = 0; + obj = nullptr; } } @@ -147,11 +147,11 @@ public: private: //============================================================================== - jobject obj = 0; + jobject obj = nullptr; static inline jobject retain (jobject obj, JNIEnv* env) { - return obj == 0 ? 0 : env->NewGlobalRef (obj); + return obj == nullptr ? nullptr : env->NewGlobalRef (obj); } }; @@ -193,7 +193,7 @@ private: size_t byteCodeSize; int minSDK; - jclass classRef = 0; + jclass classRef = nullptr; static Array& getClasses(); void initialise (JNIEnv*); @@ -523,22 +523,23 @@ DECLARE_JNI_CLASS (AndroidUri, "android/net/Uri") #undef JNI_CLASS_MEMBERS #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ - METHOD (construct, "", "(Landroid/content/Context;)V") \ - METHOD (layout, "layout", "(IIII)V") \ - METHOD (getLeft, "getLeft", "()I") \ - METHOD (getTop, "getTop", "()I") \ - METHOD (getWidth, "getWidth", "()I") \ - METHOD (getHeight, "getHeight", "()I") \ - METHOD (getLocationOnScreen, "getLocationOnScreen", "([I)V") \ - METHOD (getParent, "getParent", "()Landroid/view/ViewParent;") \ - METHOD (bringToFront, "bringToFront", "()V") \ - METHOD (requestFocus, "requestFocus", "()Z") \ - METHOD (hasFocus, "hasFocus", "()Z") \ - METHOD (invalidate, "invalidate", "(IIII)V") \ - METHOD (setVisibility, "setVisibility", "(I)V") \ - METHOD (setLayoutParams, "setLayoutParams", "(Landroid/view/ViewGroup$LayoutParams;)V") \ - METHOD (findViewById, "findViewById", "(I)Landroid/view/View;") \ - METHOD (getRootView, "getRootView", "()Landroid/view/View;") \ + METHOD (construct, "", "(Landroid/content/Context;)V") \ + METHOD (layout, "layout", "(IIII)V") \ + METHOD (getLeft, "getLeft", "()I") \ + METHOD (getTop, "getTop", "()I") \ + METHOD (getWidth, "getWidth", "()I") \ + METHOD (getHeight, "getHeight", "()I") \ + METHOD (getLocationOnScreen, "getLocationOnScreen", "([I)V") \ + METHOD (getParent, "getParent", "()Landroid/view/ViewParent;") \ + METHOD (bringToFront, "bringToFront", "()V") \ + METHOD (requestFocus, "requestFocus", "()Z") \ + METHOD (hasFocus, "hasFocus", "()Z") \ + METHOD (invalidate, "invalidate", "(IIII)V") \ + METHOD (setVisibility, "setVisibility", "(I)V") \ + METHOD (setLayoutParams, "setLayoutParams", "(Landroid/view/ViewGroup$LayoutParams;)V") \ + METHOD (setSystemUiVisibility, "setSystemUiVisibility", "(I)V") \ + METHOD (findViewById, "findViewById", "(I)Landroid/view/View;") \ + METHOD (getRootView, "getRootView", "()Landroid/view/View;") \ METHOD (addOnLayoutChangeListener, "addOnLayoutChangeListener", "(Landroid/view/View$OnLayoutChangeListener;)V") DECLARE_JNI_CLASS (AndroidView, "android/view/View") @@ -755,7 +756,7 @@ namespace { inline String juceString (JNIEnv* env, jstring s) { - if (s == 0) + if (s == nullptr) return {}; const char* const utf8 = env->GetStringUTFChars (s, nullptr); @@ -874,13 +875,29 @@ LocalRef CreateJavaInterface (AndroidInterfaceImplementer* implementer, class ActivityLifecycleCallbacks : public AndroidInterfaceImplementer { public: - virtual void onActivityCreated (jobject /*activity*/, jobject /*bundle*/) {} - virtual void onActivityDestroyed (jobject /*activity*/) {} - virtual void onActivityPaused (jobject /*activity*/) {} - virtual void onActivityResumed (jobject /*activity*/) {} - virtual void onActivitySaveInstanceState (jobject /*activity*/, jobject /*bundle*/) {} - virtual void onActivityStarted (jobject /*activity*/) {} - virtual void onActivityStopped (jobject /*activity*/) {} + virtual void onActivityPreCreated (jobject /*activity*/, jobject /*bundle*/) {} + virtual void onActivityPreDestroyed (jobject /*activity*/) {} + virtual void onActivityPrePaused (jobject /*activity*/) {} + virtual void onActivityPreResumed (jobject /*activity*/) {} + virtual void onActivityPreSaveInstanceState (jobject /*activity*/, jobject /*bundle*/) {} + virtual void onActivityPreStarted (jobject /*activity*/) {} + virtual void onActivityPreStopped (jobject /*activity*/) {} + + virtual void onActivityCreated (jobject /*activity*/, jobject /*bundle*/) {} + virtual void onActivityDestroyed (jobject /*activity*/) {} + virtual void onActivityPaused (jobject /*activity*/) {} + virtual void onActivityResumed (jobject /*activity*/) {} + virtual void onActivitySaveInstanceState (jobject /*activity*/, jobject /*bundle*/) {} + virtual void onActivityStarted (jobject /*activity*/) {} + virtual void onActivityStopped (jobject /*activity*/) {} + + virtual void onActivityPostCreated (jobject /*activity*/, jobject /*bundle*/) {} + virtual void onActivityPostDestroyed (jobject /*activity*/) {} + virtual void onActivityPostPaused (jobject /*activity*/) {} + virtual void onActivityPostResumed (jobject /*activity*/) {} + virtual void onActivityPostSaveInstanceState (jobject /*activity*/, jobject /*bundle*/) {} + virtual void onActivityPostStarted (jobject /*activity*/) {} + virtual void onActivityPostStopped (jobject /*activity*/) {} private: jobject invoke (jobject, jobject, jobjectArray) override; @@ -889,7 +906,7 @@ private: //============================================================================== struct SurfaceHolderCallback : AndroidInterfaceImplementer { - virtual ~SurfaceHolderCallback() {} + virtual ~SurfaceHolderCallback() override = default; virtual void surfaceChanged (LocalRef holder, int format, int width, int height) = 0; virtual void surfaceCreated (LocalRef holder) = 0; @@ -966,7 +983,7 @@ public: //============================================================================== // Allows you to start an activity without requiring to have an activity void startAndroidActivityForResult (const LocalRef& intent, int requestCode, - std::function)> && callback); + std::function)> && callback); //============================================================================== bool androidHasSystemFeature (const String& property); diff --git a/JuceLibraryCode/modules/juce_core/native/juce_android_Network.cpp b/JuceLibraryCode/modules/juce_core/native/juce_android_Network.cpp index 375b5fb..8808d17 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_android_Network.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_android_Network.cpp @@ -213,6 +213,62 @@ DECLARE_JNI_CLASS_WITH_BYTECODE (HTTPStream, "com/roli/juce/JuceHTTPStream", 16, DECLARE_JNI_CLASS (AndroidInputStream, "java/io/InputStream") #undef JNI_CLASS_MEMBERS +//============================================================================== +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ + METHOD (acquire, "acquire", "()V") \ + METHOD (release, "release", "()V") \ + +DECLARE_JNI_CLASS (AndroidMulticastLock, "android/net/wifi/WifiManager$MulticastLock") +#undef JNI_CLASS_MEMBERS + +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ + METHOD (createMulticastLock, "createMulticastLock", "(Ljava/lang/String;)Landroid/net/wifi/WifiManager$MulticastLock;") \ + +DECLARE_JNI_CLASS (AndroidWifiManager, "android/net/wifi/WifiManager") +#undef JNI_CLASS_MEMBERS + +static LocalRef getMulticastLock() +{ + static LocalRef multicastLock; + static bool hasChecked = false; + + if (! hasChecked) + { + hasChecked = true; + + auto* env = getEnv(); + + LocalRef wifiManager (env->CallObjectMethod (getAppContext().get(), + AndroidContext.getSystemService, + javaString ("wifi").get())); + + if (wifiManager != nullptr) + { + multicastLock = LocalRef (env->CallObjectMethod (wifiManager.get(), + AndroidWifiManager.createMulticastLock, + javaString ("JUCE_MulticastLock").get())); + } + } + + return multicastLock; +} + +JUCE_API void JUCE_CALLTYPE acquireMulticastLock() +{ + auto multicastLock = getMulticastLock(); + + if (multicastLock != nullptr) + getEnv()->CallVoidMethod (multicastLock.get(), AndroidMulticastLock.acquire); +} + +JUCE_API void JUCE_CALLTYPE releaseMulticastLock() +{ + auto multicastLock = getMulticastLock(); + + if (multicastLock != nullptr) + getEnv()->CallVoidMethod (multicastLock.get(), AndroidMulticastLock.release); +} + //============================================================================== void MACAddress::findAllAddresses (Array& /*result*/) { @@ -297,7 +353,7 @@ public: const ScopedLock lock (createStreamLock); - if (stream != 0) + if (stream != nullptr) { stream.callVoidMethod (HTTPStream.release); stream.clear(); @@ -333,7 +389,7 @@ public: if (isPost) WebInputStream::createHeadersAndPostData (url, headers, postData); - jbyteArray postDataArray = 0; + jbyteArray postDataArray = nullptr; if (postData.getSize() > 0) { @@ -348,7 +404,7 @@ public: jassert (Thread::getCurrentThread() != nullptr); jintArray statusCodeArray = env->NewIntArray (1); - jassert (statusCodeArray != 0); + jassert (statusCodeArray != nullptr); { const ScopedLock lock (createStreamLock); @@ -367,18 +423,18 @@ public: javaString (httpRequest).get()))); } - if (stream != 0 && ! stream.callBooleanMethod (HTTPStream.connect)) + if (stream != nullptr && ! stream.callBooleanMethod (HTTPStream.connect)) stream.clear(); - jint* const statusCodeElements = env->GetIntArrayElements (statusCodeArray, 0); + jint* const statusCodeElements = env->GetIntArrayElements (statusCodeArray, nullptr); statusCode = statusCodeElements[0]; env->ReleaseIntArrayElements (statusCodeArray, statusCodeElements, 0); env->DeleteLocalRef (statusCodeArray); - if (postDataArray != 0) + if (postDataArray != nullptr) env->DeleteLocalRef (postDataArray); - if (stream != 0) + if (stream != nullptr) { StringArray headerLines; @@ -547,12 +603,12 @@ static Array findIPAddresses (int dummySocket) if (item.ifr_addr.sa_family == AF_INET) { InterfaceInfo info; - info.interfaceAddress = makeAddress ((const sockaddr_in*) &item.ifr_addr); + info.interfaceAddress = makeAddress (reinterpret_cast (&item.ifr_addr)); if (! info.interfaceAddress.isNull()) { if (ioctl (dummySocket, SIOCGIFBRDADDR, &item) == 0) - info.broadcastAddress = makeAddress ((const sockaddr_in*) &item.ifr_broadaddr); + info.broadcastAddress = makeAddress (reinterpret_cast (&item.ifr_broadaddr)); result.add (info); } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_android_RuntimePermissions.cpp b/JuceLibraryCode/modules/juce_core/native/juce_android_RuntimePermissions.cpp index 8d8fa14..d4b1acf 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_android_RuntimePermissions.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_android_RuntimePermissions.cpp @@ -95,7 +95,7 @@ struct PermissionsRequest struct PermissionsOverlay : FragmentOverlay { PermissionsOverlay (CriticalSection& cs) : overlayGuard (cs) {} - ~PermissionsOverlay() {} + ~PermissionsOverlay() override = default; struct PermissionResult { @@ -175,7 +175,7 @@ struct PermissionsOverlay : FragmentOverlay // this code should only be reached for SDKs >= 23, so this method should be // be available - jassert(requestPermissionsMethodID != 0); + jassert(requestPermissionsMethodID != nullptr); env->CallVoidMethod (getNativeHandle(), requestPermissionsMethodID, jPermissionsArray.get (), 0); } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_android_Threads.cpp b/JuceLibraryCode/modules/juce_core/native/juce_android_Threads.cpp index ec1135b..1b72686 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_android_Threads.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_android_Threads.cpp @@ -113,7 +113,7 @@ public: checkActivityIsMain (androidApkContext); } - ~JuceActivityWatcher() + ~JuceActivityWatcher() override { LocalRef appContext (getAppContext()); diff --git a/JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp b/JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp index ecc4aa1..c51a97e 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp @@ -207,7 +207,7 @@ bool Process::openDocument (const String& fileName, const String& parameters) cmdString = cmdLines.joinIntoString (" || "); } - const char* const argv[4] = { "/bin/sh", "-c", cmdString.toUTF8(), 0 }; + const char* const argv[4] = { "/bin/sh", "-c", cmdString.toUTF8(), nullptr }; auto cpid = fork(); diff --git a/JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp b/JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp index e02a369..61e6f3a 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp @@ -25,14 +25,15 @@ namespace juce void MACAddress::findAllAddresses (Array& result) { - const int s = socket (AF_INET, SOCK_DGRAM, 0); + auto s = socket (AF_INET, SOCK_DGRAM, 0); + if (s != -1) { struct ifaddrs* addrs = nullptr; if (getifaddrs (&addrs) != -1) { - for (struct ifaddrs* i = addrs; i != nullptr; i = i->ifa_next) + for (auto* i = addrs; i != nullptr; i = i->ifa_next) { struct ifreq ifr; strcpy (ifr.ifr_name, i->ifa_name); @@ -69,7 +70,7 @@ bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& /* targetEma class WebInputStream::Pimpl { public: - Pimpl (WebInputStream& pimplOwner, const URL& urlToCopy, const bool shouldUsePost) + Pimpl (WebInputStream& pimplOwner, const URL& urlToCopy, bool shouldUsePost) : owner (pimplOwner), url (urlToCopy), isPost (shouldUsePost), httpRequestCmd (shouldUsePost ? "POST" : "GET") {} @@ -95,19 +96,21 @@ public: void withCustomRequestCommand (const String& customRequestCommand) { httpRequestCmd = customRequestCommand; } void withConnectionTimeout (int timeoutInMs) { timeOutMs = timeoutInMs; } void withNumRedirectsToFollow (int maxRedirectsToFollow) { numRedirectsToFollow = maxRedirectsToFollow; } + int getStatusCode() const { return statusCode; } StringPairArray getRequestHeaders() const { return WebInputStream::parseHttpHeaders (headers); } StringPairArray getResponseHeaders() const { StringPairArray responseHeaders; + if (! isError()) { for (int i = 0; i < headerLines.size(); ++i) { - const String& headersEntry = headerLines[i]; - const String key (headersEntry.upToFirstOccurrenceOf (": ", false, false)); - const String value (headersEntry.fromFirstOccurrenceOf (": ", false, false)); - const String previousValue (responseHeaders [key]); + auto& headersEntry = headerLines[i]; + auto key = headersEntry.upToFirstOccurrenceOf (": ", false, false); + auto value = headersEntry.fromFirstOccurrenceOf (": ", false, false); + auto previousValue = responseHeaders[key]; responseHeaders.set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); } } @@ -115,8 +118,6 @@ public: return responseHeaders; } - int getStatusCode() const { return statusCode; } - bool connect (WebInputStream::Listener* listener) { { @@ -129,7 +130,7 @@ public: address = url.toString (! isPost); statusCode = createConnection (listener, numRedirectsToFollow); - return (statusCode != 0); + return statusCode != 0; } void cancel() @@ -137,7 +138,6 @@ public: const ScopedLock lock (createSocketLock); hasBeenCancelled = true; - statusCode = -1; finished = true; @@ -190,7 +190,7 @@ public: chunkLengthBuffer.writeByte (c); } - const int64 chunkSize = chunkLengthBuffer.toString().trimStart().getHexValue64(); + auto chunkSize = chunkLengthBuffer.toString().trimStart().getHexValue64(); if (chunkSize == 0) { @@ -205,18 +205,13 @@ public: bytesToRead = static_cast (chunkEnd - position); } - fd_set readbits; - FD_ZERO (&readbits); - FD_SET (socketHandle, &readbits); + pollfd pfd { socketHandle, POLLIN, 0 }; - struct timeval tv; - tv.tv_sec = jmax (1, timeOutMs / 1000); - tv.tv_usec = 0; + if (poll (&pfd, 1, timeOutMs) <= 0) + return 0; // (timeout) - if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) - return 0; // (timeout) + auto bytesRead = jmax (0, (int) recv (socketHandle, buffer, (size_t) bytesToRead, MSG_WAITALL)); - const int bytesRead = jmax (0, (int) recv (socketHandle, buffer, (size_t) bytesToRead, MSG_WAITALL)); if (bytesRead == 0) finished = true; @@ -238,7 +233,7 @@ public: if (wantedPos < position) return false; - int64 numBytesToSkip = wantedPos - position; + auto numBytesToSkip = wantedPos - position; auto skipBufferSize = (int) jmin (numBytesToSkip, (int64) 16384); HeapBlock temp (skipBufferSize); @@ -286,14 +281,14 @@ private: levelsOfRedirection = 0; } - int createConnection (WebInputStream::Listener* listener, const int numRedirects) + int createConnection (WebInputStream::Listener* listener, int numRedirects) { closeSocket (false); if (isPost) WebInputStream::createHeadersAndPostData (url, headers, postData); - uint32 timeOutTime = Time::getMillisecondCounter(); + auto timeOutTime = Time::getMillisecondCounter(); if (timeOutMs == 0) timeOutMs = 30000; @@ -305,6 +300,7 @@ private: String hostName, hostPath; int hostPort; + if (! decomposeURL (address, hostName, hostPath, hostPort)) return 0; @@ -312,7 +308,8 @@ private: int proxyPort = 0; int port = 0; - const String proxyURL (getenv ("http_proxy")); + auto proxyURL = String::fromUTF8 (getenv ("http_proxy")); + if (proxyURL.startsWithIgnoreCase ("http://")) { if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort)) @@ -335,6 +332,7 @@ private: hints.ai_flags = AI_NUMERICSERV; struct addrinfo* result = nullptr; + if (getaddrinfo (serverName.toUTF8(), String (port).toUTF8(), &hints, &result) != 0 || result == 0) return 0; @@ -379,17 +377,17 @@ private: } } - String responseHeader (readResponse (timeOutTime)); + auto responseHeader = readResponse (timeOutTime); position = 0; if (responseHeader.isNotEmpty()) { headerLines = StringArray::fromLines (responseHeader); - const int status = responseHeader.fromFirstOccurrenceOf (" ", false, false) - .substring (0, 3).getIntValue(); + auto status = responseHeader.fromFirstOccurrenceOf (" ", false, false) + .substring (0, 3).getIntValue(); - String location (findHeaderItem (headerLines, "Location:")); + auto location = findHeaderItem (headerLines, "Location:"); if (++levelsOfRedirection <= numRedirects && status >= 300 && status < 400 @@ -410,7 +408,7 @@ private: return createConnection (listener, numRedirects); } - String contentLengthString (findHeaderItem (headerLines, "Content-Length:")); + auto contentLengthString = findHeaderItem (headerLines, "Content-Length:"); if (contentLengthString.isNotEmpty()) contentLength = contentLengthString.getLargeIntValue(); @@ -425,7 +423,7 @@ private: } //============================================================================== - String readResponse (const uint32 timeOutTime) + String readResponse (uint32 timeOutTime) { int numConsecutiveLFs = 0; MemoryOutputStream buffer; @@ -436,6 +434,7 @@ private: && ! (finished || isError())) { char c = 0; + if (read (&c, 1) != 1) return {}; @@ -447,7 +446,7 @@ private: numConsecutiveLFs = 0; } - const String header (buffer.toString().trimEnd()); + auto header = buffer.toString().trimEnd(); if (header.startsWithIgnoreCase ("HTTP/")) return header; @@ -471,11 +470,11 @@ private: dest << ':' << port; } - static MemoryBlock createRequestHeader (const String& hostName, const int hostPort, - const String& proxyName, const int proxyPort, + static MemoryBlock createRequestHeader (const String& hostName, int hostPort, + const String& proxyName, int proxyPort, const String& hostPath, const String& originalURL, const String& userHeaders, const MemoryBlock& postData, - const bool isPost, const String& httpRequestCmd) + bool isPost, const String& httpRequestCmd) { MemoryOutputStream header; @@ -503,7 +502,7 @@ private: return header.getMemoryBlock(); } - static bool sendHeader (int socketHandle, const MemoryBlock& requestHeader, const uint32 timeOutTime, + static bool sendHeader (int socketHandle, const MemoryBlock& requestHeader, uint32 timeOutTime, WebInputStream& pimplOwner, WebInputStream::Listener* listener) { size_t totalHeaderSent = 0; @@ -513,7 +512,7 @@ private: if (Time::getMillisecondCounter() > timeOutTime) return false; - const int numToSend = jmin (1024, (int) (requestHeader.getSize() - totalHeaderSent)); + auto numToSend = jmin (1024, (int) (requestHeader.getSize() - totalHeaderSent)); if (send (socketHandle, static_cast (requestHeader.getData()) + totalHeaderSent, (size_t) numToSend, 0) != numToSend) return false; @@ -532,8 +531,9 @@ private: if (! url.startsWithIgnoreCase ("http://")) return false; - const int nextSlash = url.indexOfChar (7, '/'); - int nextColon = url.indexOfChar (7, ':'); + auto nextSlash = url.indexOfChar (7, '/'); + auto nextColon = url.indexOfChar (7, ':'); + if (nextColon > nextSlash && nextSlash > 0) nextColon = -1; diff --git a/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp b/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp index 01a3428..7ba2b5e 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp @@ -20,6 +20,10 @@ ============================================================================== */ +#if JUCE_BELA +extern "C" int cobalt_thread_mode(); +#endif + namespace juce { @@ -103,11 +107,11 @@ int SystemStats::getPageSize() //============================================================================== String SystemStats::getLogonName() { - if (const char* user = getenv ("USER")) - return CharPointer_UTF8 (user); + if (auto user = getenv ("USER")) + return String::fromUTF8 (user); - if (struct passwd* const pw = getpwuid (getuid())) - return CharPointer_UTF8 (pw->pw_name); + if (auto pw = getpwuid (getuid())) + return String::fromUTF8 (pw->pw_name); return {}; } @@ -119,7 +123,8 @@ String SystemStats::getFullUserName() String SystemStats::getComputerName() { - char name [256] = { 0 }; + char name[256] = {}; + if (gethostname (name, sizeof (name) - 1) == 0) return name; @@ -128,21 +133,24 @@ String SystemStats::getComputerName() static String getLocaleValue (nl_item key) { - const char* oldLocale = ::setlocale (LC_ALL, ""); - String result (String::fromUTF8 (nl_langinfo (key))); + auto oldLocale = ::setlocale (LC_ALL, ""); + auto result = String::fromUTF8 (nl_langinfo (key)); ::setlocale (LC_ALL, oldLocale); return result; } -String SystemStats::getUserLanguage() { return getLocaleValue (_NL_IDENTIFICATION_LANGUAGE); } -String SystemStats::getUserRegion() { return getLocaleValue (_NL_IDENTIFICATION_TERRITORY); } -String SystemStats::getDisplayLanguage() { return getUserLanguage() + "-" + getUserRegion(); } +String SystemStats::getUserLanguage() { return getLocaleValue (_NL_IDENTIFICATION_LANGUAGE); } +String SystemStats::getUserRegion() { return getLocaleValue (_NL_IDENTIFICATION_TERRITORY); } +String SystemStats::getDisplayLanguage() { return getUserLanguage() + "-" + getUserRegion(); } //============================================================================== void CPUInformation::initialise() noexcept { auto flags = getCpuInfo ("flags"); + hasMMX = flags.contains ("mmx"); + hasFMA3 = flags.contains ("fma"); + hasFMA4 = flags.contains ("fma4"); hasSSE = flags.contains ("sse"); hasSSE2 = flags.contains ("sse2"); hasSSE3 = flags.contains ("sse3"); @@ -175,16 +183,21 @@ void CPUInformation::initialise() noexcept //============================================================================== uint32 juce_millisecondsSinceStartup() noexcept { - timespec t; - clock_gettime (CLOCK_MONOTONIC, &t); - - return (uint32) (t.tv_sec * 1000 + t.tv_nsec / 1000000); + return (uint32) (Time::getHighResolutionTicks() / 1000); } int64 Time::getHighResolutionTicks() noexcept { timespec t; + + #if JUCE_BELA + if (cobalt_thread_mode() == 0x200 /*XNRELAX*/) + clock_gettime (CLOCK_MONOTONIC, &t); + else + __wrap_clock_gettime (CLOCK_MONOTONIC, &t); + #else clock_gettime (CLOCK_MONOTONIC, &t); + #endif return (t.tv_sec * (int64) 1000000) + (t.tv_nsec / 1000); } @@ -205,7 +218,7 @@ bool Time::setSystemTimeToThisTime() const t.tv_sec = millisSinceEpoch / 1000; t.tv_usec = (millisSinceEpoch - t.tv_sec * 1000) * 1000; - return settimeofday (&t, 0) == 0; + return settimeofday (&t, nullptr) == 0; } JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept @@ -213,8 +226,7 @@ JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept #if JUCE_BSD return false; #else - return readPosixConfigFileValue ("/proc/self/status", "TracerPid") - .getIntValue() > 0; + return readPosixConfigFileValue ("/proc/self/status", "TracerPid").getIntValue() > 0; #endif } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_linux_Threads.cpp b/JuceLibraryCode/modules/juce_core/native/juce_linux_Threads.cpp index 144fa05..11ea9c5 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_linux_Threads.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_linux_Threads.cpp @@ -31,9 +31,9 @@ namespace juce //============================================================================== JUCE_API void JUCE_CALLTYPE Process::setPriority (const ProcessPriority prior) { - const int policy = (prior <= NormalPriority) ? SCHED_OTHER : SCHED_RR; - const int minp = sched_get_priority_min (policy); - const int maxp = sched_get_priority_max (policy); + auto policy = (prior <= NormalPriority) ? SCHED_OTHER : SCHED_RR; + auto minp = sched_get_priority_min (policy); + auto maxp = sched_get_priority_max (policy); struct sched_param param; @@ -51,8 +51,8 @@ JUCE_API void JUCE_CALLTYPE Process::setPriority (const ProcessPriority prior) static bool swapUserAndEffectiveUser() { - int result1 = setreuid (geteuid(), getuid()); - int result2 = setregid (getegid(), getgid()); + auto result1 = setreuid (geteuid(), getuid()); + auto result2 = setregid (getegid(), getgid()); return result1 == 0 && result2 == 0; } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_ClangBugWorkaround.h b/JuceLibraryCode/modules/juce_core/native/juce_mac_ClangBugWorkaround.h index 3bbee9a..eb02c17 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_mac_ClangBugWorkaround.h +++ b/JuceLibraryCode/modules/juce_core/native/juce_mac_ClangBugWorkaround.h @@ -20,10 +20,10 @@ ============================================================================== */ - -// 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__)) + + // 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.. #include #undef CF_OPTIONS #define CF_OPTIONS(_type, _name) _type _name; enum @@ -32,4 +32,10 @@ // in the live-build engine. #define _Nullable #define _Nonnull + + // A workaround for compiling the 10.15 headers with an older compiler version + #undef API_UNAVAILABLE_BEGIN + #define API_UNAVAILABLE_BEGIN(...) + #undef API_UNAVAILABLE_END + #define API_UNAVAILABLE_END #endif diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm b/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm index 55e2a4e..5303121 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm +++ b/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm @@ -36,15 +36,9 @@ bool File::copyInternal (const File& dest) const NSFileManager* fm = [NSFileManager defaultManager]; return [fm fileExistsAtPath: juceStringToNS (fullPath)] - #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 && [fm copyItemAtPath: juceStringToNS (fullPath) toPath: juceStringToNS (dest.getFullPathName()) error: nil]; - #else - && [fm copyPath: juceStringToNS (fullPath) - toPath: juceStringToNS (dest.getFullPathName()) - handler: nil]; - #endif } } @@ -75,7 +69,7 @@ namespace MacFileHelpers static bool isHiddenFile (const String& path) { - #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6 + #if JUCE_MAC JUCE_AUTORELEASEPOOL { NSNumber* hidden = nil; @@ -84,15 +78,8 @@ namespace MacFileHelpers return [createNSURLFromFile (path) getResourceValue: &hidden forKey: NSURLIsHiddenKey error: &err] && [hidden boolValue]; } - #elif JUCE_IOS - return File (path).getFileName().startsWithChar ('.'); #else - FSRef ref; - LSItemInfoRecord info; - - return FSPathMakeRefWithOptions ((const UInt8*) path.toRawUTF8(), kFSPathMakeRefDoNotFollowLeafSymlink, &ref, 0) == noErr - && LSCopyItemInfoForRef (&ref, kLSRequestBasicFlagsOnly, &info) == noErr - && (info.flags & kLSItemInfoIsInvisible) != 0; + return File (path).getFileName().startsWithChar ('.'); #endif } @@ -428,10 +415,7 @@ bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String& NSWorkspace* workspace = [NSWorkspace sharedWorkspace]; if (parameters.isEmpty()) - // NB: the length check here is because of strange failures involving long filenames, - // probably due to filesystem name length limitations.. - return (fileName.length() < 1024 && [workspace openFile: juceStringToNS (fileName)]) - || [workspace openURL: filenameAsURL]; + return [workspace openURL: filenameAsURL]; const File file (fileName); diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm b/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm index 0730ba1..d86baf4 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm +++ b/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm @@ -110,7 +110,7 @@ bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& targetEmailA //============================================================================== // Unfortunately, we need to have this ugly ifdef here as long as some older OS X versions do not support NSURLSession -#if JUCE_IOS || (defined (__MAC_OS_X_VERSION_MIN_REQUIRED) && defined (__MAC_10_10) && __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_10) +#if JUCE_IOS || (defined (MAC_OS_X_VERSION_10_10) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10) //============================================================================== class URLConnectionState : private Thread @@ -331,8 +331,8 @@ public: NSMutableData* data = nil; NSDictionary* headers = nil; int statusCode = 0; - std::atomic initialised { false }; - bool hasFailed = false, hasFinished = false, isBeingDeleted = false; + std::atomic initialised { false }, hasFailed { false }, hasFinished { false }; + bool isBeingDeleted = false; const int numRedirectsToFollow; int numRedirects = 0; int64 latestTotalBytes = 0; @@ -425,7 +425,8 @@ struct BackgroundDownloadTask : public URL::DownloadTask DelegateClass::setState (delegate, this); activeSessions.set (uniqueIdentifier, this); - NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:juceStringToNS (urlToUse.toString (true))]]; + auto nsUrl = [NSURL URLWithString: juceStringToNS (urlToUse.toString (true))]; + NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL: nsUrl]; if (shouldUsePostRequest) [request setHTTPMethod: @"POST"]; @@ -965,10 +966,13 @@ public: createConnection(); } + if (connection == nullptr) + return false; + if (! connection->start (owner, webInputListener)) { // Workaround for deployment targets below 10.10 where HTTPS POST requests with keep-alive fail with the NSURLErrorNetworkConnectionLost error code. - #if ! (JUCE_IOS || (defined (__MAC_OS_X_VERSION_MIN_REQUIRED) && defined (__MAC_10_10) && __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_10)) + #if ! (JUCE_IOS || (defined (MAC_OS_X_VERSION_10_10) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10)) if (numRetries == 0 && connection->nsUrlErrorCode == NSURLErrorNetworkConnectionLost) { connection.reset(); @@ -980,7 +984,7 @@ public: return false; } - if (connection != nullptr && connection->headers != nil) + if (connection->headers != nil) { statusCode = connection->statusCode; diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm b/JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm index 4fc323d..5888860 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm +++ b/JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm @@ -81,10 +81,14 @@ void CPUInformation::initialise() noexcept has3DNow = (b & (1u << 31)) != 0; hasSSE3 = (c & (1u << 0)) != 0; hasSSSE3 = (c & (1u << 9)) != 0; + hasFMA3 = (c & (1u << 12)) != 0; hasSSE41 = (c & (1u << 19)) != 0; hasSSE42 = (c & (1u << 20)) != 0; hasAVX = (c & (1u << 28)) != 0; + SystemStatsHelpers::doCPUID (a, b, c, d, 0x80000001); + hasFMA4 = (c & (1u << 16)) != 0; + SystemStatsHelpers::doCPUID (a, b, c, d, 7); hasAVX2 = (b & (1u << 5)) != 0; hasAVX512F = (b & (1u << 16)) != 0; @@ -159,11 +163,28 @@ String SystemStats::getDeviceDescription() #endif size_t size; + if (sysctlbyname (name, nullptr, &size, nullptr, 0) >= 0) { HeapBlock model (size); - if (sysctlbyname (name, model, &size, nullptr, 0) >= 0) - return model.get(); + + if (sysctlbyname (name, model, &size, nullptr, 0) >= 0) + { + String description (model.get()); + + #if JUCE_IOS + if (description == "x86_64") // running in the simulator + { + if (auto* userInfo = [[NSProcessInfo processInfo] environment]) + { + if (auto* simDeviceName = [userInfo objectForKey: @"SIMULATOR_DEVICE_NAME"]) + return nsStringToJuce (simDeviceName); + } + } + #endif + + return description; + } } return {}; diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_Threads.mm b/JuceLibraryCode/modules/juce_core/native/juce_mac_Threads.mm index 36943c7..bd8084c 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_mac_Threads.mm +++ b/JuceLibraryCode/modules/juce_core/native/juce_mac_Threads.mm @@ -65,20 +65,10 @@ JUCE_API void JUCE_CALLTYPE Process::hide() } } -JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() -{ - jassertfalse; -} +JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() {} +JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() {} -JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() -{ - jassertfalse; -} - -JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority) -{ - // xxx -} +JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority) {} //============================================================================== JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept diff --git a/JuceLibraryCode/modules/juce_core/native/juce_osx_ObjCHelpers.h b/JuceLibraryCode/modules/juce_core/native/juce_osx_ObjCHelpers.h index d98af4f..fca4692 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_osx_ObjCHelpers.h +++ b/JuceLibraryCode/modules/juce_core/native/juce_osx_ObjCHelpers.h @@ -412,5 +412,19 @@ private: BlockType block; }; +struct ScopedCFString +{ + ScopedCFString() = default; + ScopedCFString (String s) : cfString (s.toCFString()) {} + + ~ScopedCFString() noexcept + { + if (cfString != nullptr) + CFRelease (cfString); + } + + CFStringRef cfString = {}; +}; + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_posix_NamedPipe.cpp b/JuceLibraryCode/modules/juce_core/native/juce_posix_NamedPipe.cpp index af8b396..72b810a 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_posix_NamedPipe.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_posix_NamedPipe.cpp @@ -172,15 +172,8 @@ private: static void waitForInput (int handle, int timeoutMsecs) noexcept { - struct timeval timeout; - timeout.tv_sec = timeoutMsecs / 1000; - timeout.tv_usec = (timeoutMsecs % 1000) * 1000; - - fd_set rset; - FD_ZERO (&rset); - FD_SET (handle, &rset); - - select (handle + 1, &rset, nullptr, nullptr, &timeout); + pollfd pfd { handle, POLLIN, 0 }; + poll (&pfd, 1, timeoutMsecs); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) diff --git a/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h b/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h index d833c89..5d30962 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h +++ b/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h @@ -40,95 +40,6 @@ void CriticalSection::enter() const noexcept { pthread_mutex_lock (&lock) bool CriticalSection::tryEnter() const noexcept { return pthread_mutex_trylock (&lock) == 0; } void CriticalSection::exit() const noexcept { pthread_mutex_unlock (&lock); } -//============================================================================== -WaitableEvent::WaitableEvent (bool useManualReset) noexcept - : triggered (false), manualReset (useManualReset) -{ - pthread_cond_init (&condition, {}); - - pthread_mutexattr_t atts; - pthread_mutexattr_init (&atts); - #if ! JUCE_ANDROID - pthread_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT); - #endif - pthread_mutex_init (&mutex, &atts); - pthread_mutexattr_destroy (&atts); -} - -WaitableEvent::~WaitableEvent() noexcept -{ - pthread_cond_destroy (&condition); - pthread_mutex_destroy (&mutex); -} - -bool WaitableEvent::wait (int timeOutMillisecs) const noexcept -{ - pthread_mutex_lock (&mutex); - - if (! triggered) - { - if (timeOutMillisecs < 0) - { - do - { - pthread_cond_wait (&condition, &mutex); - } - while (! triggered); - } - else - { - struct timeval now; - gettimeofday (&now, nullptr); - - struct timespec time; - time.tv_sec = now.tv_sec + (timeOutMillisecs / 1000); - time.tv_nsec = (now.tv_usec + ((timeOutMillisecs % 1000) * 1000)) * 1000; - - if (time.tv_nsec >= 1000000000) - { - time.tv_nsec -= 1000000000; - time.tv_sec++; - } - - do - { - if (pthread_cond_timedwait (&condition, &mutex, &time) == ETIMEDOUT) - { - pthread_mutex_unlock (&mutex); - return false; - } - } - while (! triggered); - } - } - - if (! manualReset) - triggered = false; - - pthread_mutex_unlock (&mutex); - return true; -} - -void WaitableEvent::signal() const noexcept -{ - pthread_mutex_lock (&mutex); - - if (! triggered) - { - triggered = true; - pthread_cond_broadcast (&condition); - } - - pthread_mutex_unlock (&mutex); -} - -void WaitableEvent::reset() const noexcept -{ - pthread_mutex_lock (&mutex); - triggered = false; - pthread_mutex_unlock (&mutex); -} - //============================================================================== void JUCE_CALLTYPE Thread::sleep (int millisecs) { @@ -271,7 +182,7 @@ namespace return statfs (f.getFullPathName().toUTF8(), &result) == 0; } - #if (JUCE_MAC && MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5) || JUCE_IOS + #if JUCE_MAC || JUCE_IOS static int64 getCreationTime (const juce_statStruct& s) noexcept { return (int64) s.st_birthtime; } #else static int64 getCreationTime (const juce_statStruct& s) noexcept { return (int64) s.st_ctime; } @@ -400,13 +311,19 @@ void File::getFileTimesInternal (int64& modificationTime, int64& accessTime, int if (juce_stat (fullPath, info)) { + #if JUCE_MAC || (JUCE_IOS && __DARWIN_ONLY_64_BIT_INO_T) + modificationTime = (int64) info.st_mtimespec.tv_sec * 1000 + info.st_mtimespec.tv_nsec / 1000000; + accessTime = (int64) info.st_atimespec.tv_sec * 1000 + info.st_atimespec.tv_nsec / 1000000; + creationTime = (int64) info.st_birthtimespec.tv_sec * 1000 + info.st_birthtimespec.tv_nsec / 1000000; + #else modificationTime = (int64) info.st_mtime * 1000; accessTime = (int64) info.st_atime * 1000; - #if (JUCE_MAC && MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5) || JUCE_IOS + #if JUCE_IOS creationTime = (int64) info.st_birthtime * 1000; #else creationTime = (int64) info.st_ctime * 1000; #endif + #endif } } @@ -416,11 +333,32 @@ bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64 if ((modificationTime != 0 || accessTime != 0) && juce_stat (fullPath, info)) { + #if JUCE_MAC || (JUCE_IOS && __DARWIN_ONLY_64_BIT_INO_T) + struct timeval times[2]; + + bool setModificationTime = (modificationTime != 0); + bool setAccessTime = (accessTime != 0); + + times[0].tv_sec = setAccessTime ? static_cast<__darwin_time_t> (accessTime / 1000) + : info.st_atimespec.tv_sec; + + times[0].tv_usec = setAccessTime ? static_cast<__darwin_suseconds_t> ((accessTime % 1000) * 1000) + : static_cast<__darwin_suseconds_t> (info.st_atimespec.tv_nsec / 1000); + + times[1].tv_sec = setModificationTime ? static_cast<__darwin_time_t> (modificationTime / 1000) + : info.st_mtimespec.tv_sec; + + times[1].tv_usec = setModificationTime ? static_cast<__darwin_suseconds_t> ((modificationTime % 1000) * 1000) + : static_cast<__darwin_suseconds_t> (info.st_mtimespec.tv_nsec / 1000); + + return utimes (fullPath.toUTF8(), times) == 0; + #else struct utimbuf times; times.actime = accessTime != 0 ? static_cast (accessTime / 1000) : static_cast (info.st_atime); times.modtime = modificationTime != 0 ? static_cast (modificationTime / 1000) : static_cast (info.st_mtime); return utime (fullPath.toUTF8(), ×) == 0; + #endif } return false; @@ -887,8 +825,7 @@ void JUCE_API juce_threadEntryPoint (void*); extern JavaVM* androidJNIJavaVM; #endif -extern "C" void* threadEntryProc (void*); -extern "C" void* threadEntryProc (void* userData) +static void* threadEntryProc (void* userData) { auto* myself = static_cast (userData); @@ -1044,8 +981,8 @@ void JUCE_CALLTYPE Thread::setCurrentThreadAffinityMask (uint32 affinityMask) CPU_ZERO (&affinity); for (int i = 0; i < 32; ++i) - if ((affinityMask & (1 << i)) != 0) - CPU_SET (i, &affinity); + if ((affinityMask & (uint32) (1 << i)) != 0) + CPU_SET ((size_t) i, &affinity); #if (! JUCE_ANDROID) && ((! JUCE_LINUX) || ((__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2004)) pthread_setaffinity_np (pthread_self(), sizeof (cpu_set_t), &affinity); @@ -1155,7 +1092,7 @@ public: argv.add (nullptr); execvp (exe.toRawUTF8(), argv.getRawDataPointer()); - exit (-1); + _exit (-1); } else { @@ -1176,14 +1113,24 @@ public: close (pipeHandle); } - bool isRunning() const noexcept + bool isRunning() noexcept { if (childPID == 0) return false; int childState; auto pid = waitpid (childPID, &childState, WNOHANG); - return pid == 0 || ! (WIFEXITED (childState) || WIFSIGNALED (childState)); + + if (pid == 0) + return true; + + if (WIFEXITED (childState)) + { + exitCode = WEXITSTATUS (childState); + return false; + } + + return ! WIFSIGNALED (childState); } int read (void* dest, int numBytes) noexcept @@ -1198,7 +1145,21 @@ public: readHandle = fdopen (pipeHandle, "r"); if (readHandle != nullptr) - return (int) fread (dest, 1, (size_t) numBytes, readHandle); + { + for (;;) + { + auto numBytesRead = (int) fread (dest, 1, (size_t) numBytes, readHandle); + + if (numBytesRead > 0 || feof (readHandle)) + return numBytesRead; + + // signal occurred during fread() so try again + if (ferror (readHandle) && errno == EINTR) + continue; + + break; + } + } return 0; } @@ -1208,15 +1169,21 @@ public: return ::kill (childPID, SIGKILL) == 0; } - uint32 getExitCode() const noexcept + uint32 getExitCode() noexcept { + if (exitCode >= 0) + return (uint32) exitCode; + if (childPID != 0) { int childState = 0; auto pid = waitpid (childPID, &childState, WNOHANG); if (pid >= 0 && WIFEXITED (childState)) - return WEXITSTATUS (childState); + { + exitCode = WEXITSTATUS (childState); + return (uint32) exitCode; + } } return 0; @@ -1224,6 +1191,7 @@ public: int childPID = 0; int pipeHandle = 0; + int exitCode = -1; FILE* readHandle = {}; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ActiveProcess) @@ -1321,7 +1289,7 @@ struct HighResolutionTimer::Pimpl } HighResolutionTimer& owner; - std::atomic periodMs; + std::atomic periodMs { 0 }; private: pthread_t thread = {}; diff --git a/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp b/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp index 9fd1e93..bdc5029 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp @@ -544,7 +544,12 @@ uint64 File::getFileIdentifier() const { uint64 result = 0; - auto h = CreateFile (getFullPathName().toWideCharPointer(), + String path = getFullPathName(); + + if (isRoot()) + path += "\\"; + + auto h = CreateFile (path.toWideCharPointer(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); @@ -575,10 +580,8 @@ bool File::isOnHardDisk() const auto n = WindowsFileHelpers::getWindowsDriveType (getFullPathName()); - if (fullPath.toLowerCase()[0] <= 'b' && fullPath[1] == ':') - return n != DRIVE_REMOVABLE; - - return n != DRIVE_CDROM + return n != DRIVE_REMOVABLE + && n != DRIVE_CDROM && n != DRIVE_REMOTE && n != DRIVE_NO_ROOT_DIR; } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_win32_Registry.cpp b/JuceLibraryCode/modules/juce_core/native/juce_win32_Registry.cpp index e1b5f5b..afd2982 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_win32_Registry.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_win32_Registry.cpp @@ -49,7 +49,7 @@ struct RegistryKeyWrapper ~RegistryKeyWrapper() { - if (key != 0) + if (key != nullptr) RegCloseKey (key); } @@ -73,7 +73,7 @@ struct RegistryKeyWrapper { const RegistryKeyWrapper key (regValuePath, true, wow64Flags); - return key.key != 0 + return key.key != nullptr && RegSetValueEx (key.key, key.wideCharValueName, 0, type, reinterpret_cast (data), (DWORD) dataSize) == ERROR_SUCCESS; @@ -83,7 +83,7 @@ struct RegistryKeyWrapper { const RegistryKeyWrapper key (regValuePath, false, wow64Flags); - if (key.key != 0) + if (key.key != nullptr) { for (unsigned long bufferSize = 1024; ; bufferSize *= 2) { @@ -121,16 +121,16 @@ struct RegistryKeyWrapper return defaultValue; } - static bool keyExists (const String& regValuePath, const DWORD wow64Flags) + static bool keyExists (const String& regKeyPath, const DWORD wow64Flags) { - return RegistryKeyWrapper (regValuePath, false, wow64Flags).key != 0; + return RegistryKeyWrapper (regKeyPath + "\\", false, wow64Flags).key != nullptr; } static bool valueExists (const String& regValuePath, const DWORD wow64Flags) { const RegistryKeyWrapper key (regValuePath, false, wow64Flags); - if (key.key == 0) + if (key.key == nullptr) return false; unsigned char buffer [512]; @@ -143,7 +143,7 @@ struct RegistryKeyWrapper return result == ERROR_SUCCESS || result == ERROR_MORE_DATA; } - HKEY key = 0; + HKEY key = nullptr; const wchar_t* wideCharValueName = nullptr; String valueName; @@ -186,23 +186,23 @@ bool JUCE_CALLTYPE WindowsRegistry::valueExists (const String& regValuePath, WoW return RegistryKeyWrapper::valueExists (regValuePath, (DWORD) mode); } -bool JUCE_CALLTYPE WindowsRegistry::keyExists (const String& regValuePath, WoW64Mode mode) +bool JUCE_CALLTYPE WindowsRegistry::keyExists (const String& regKeyPath, WoW64Mode mode) { - return RegistryKeyWrapper::keyExists (regValuePath, (DWORD) mode); + return RegistryKeyWrapper::keyExists (regKeyPath, (DWORD) mode); } bool JUCE_CALLTYPE WindowsRegistry::deleteValue (const String& regValuePath, WoW64Mode mode) { const RegistryKeyWrapper key (regValuePath, true, (DWORD) mode); - return key.key != 0 && RegDeleteValue (key.key, key.wideCharValueName) == ERROR_SUCCESS; + return key.key != nullptr && RegDeleteValue (key.key, key.wideCharValueName) == ERROR_SUCCESS; } static bool deleteKeyNonRecursive (const String& regKeyPath, WindowsRegistry::WoW64Mode mode) { const RegistryKeyWrapper key (regKeyPath, true, (DWORD) mode); - return key.key != 0 && RegDeleteKey (key.key, key.wideCharValueName) == ERROR_SUCCESS; + return key.key != nullptr && RegDeleteKey (key.key, key.wideCharValueName) == ERROR_SUCCESS; } bool JUCE_CALLTYPE WindowsRegistry::deleteKey (const String& regKeyPath, WoW64Mode mode) diff --git a/JuceLibraryCode/modules/juce_core/native/juce_win32_SystemStats.cpp b/JuceLibraryCode/modules/juce_core/native/juce_win32_SystemStats.cpp index d9a8408..bbdd258 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_win32_SystemStats.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_win32_SystemStats.cpp @@ -23,7 +23,7 @@ namespace juce { -#if ! JUCE_MINGW +#if JUCE_MSVC #pragma intrinsic (__cpuid) #pragma intrinsic (__rdtsc) #endif @@ -41,7 +41,7 @@ void Logger::outputDebugString (const String& text) //============================================================================== -#if JUCE_MINGW +#if JUCE_MINGW || JUCE_CLANG static void callCPUID (int result[4], uint32 type) { uint32 la = result[0], lb = result[1], lc = result[2], ld = result[3]; @@ -144,11 +144,15 @@ void CPUInformation::initialise() noexcept hasSSE2 = (info[3] & (1 << 26)) != 0; hasSSE3 = (info[2] & (1 << 0)) != 0; hasAVX = (info[2] & (1 << 28)) != 0; + hasFMA3 = (info[2] & (1 << 12)) != 0; hasSSSE3 = (info[2] & (1 << 9)) != 0; hasSSE41 = (info[2] & (1 << 19)) != 0; hasSSE42 = (info[2] & (1 << 20)) != 0; has3DNow = (info[1] & (1 << 31)) != 0; + callCPUID (info, 0x80000001); + hasFMA4 = (info[2] & (1 << 16)) != 0; + callCPUID (info, 7); hasAVX2 = (info[1] & (1 << 5)) != 0; @@ -185,43 +189,73 @@ static DebugFlagsInitialiser debugFlagsInitialiser; #endif //============================================================================== -static uint32 getWindowsVersion() -{ - auto filename = _T("kernel32.dll"); - DWORD handle = 0; +#if JUCE_MINGW + static uint32 getWindowsVersion() + { + auto filename = _T("kernel32.dll"); + DWORD handle = 0; - if (auto size = GetFileVersionInfoSize (filename, &handle)) - { - HeapBlock data (size); + if (auto size = GetFileVersionInfoSize (filename, &handle)) + { + HeapBlock data (size); - if (GetFileVersionInfo (filename, handle, size, data)) - { - VS_FIXEDFILEINFO* info = nullptr; - UINT verSize = 0; + if (GetFileVersionInfo (filename, handle, size, data)) + { + VS_FIXEDFILEINFO* info = nullptr; + UINT verSize = 0; - if (VerQueryValue (data, (LPCTSTR) _T("\\"), (void**) &info, &verSize)) - if (size > 0 && info != nullptr && info->dwSignature == 0xfeef04bd) - return (uint32) info->dwFileVersionMS; - } - } + if (VerQueryValue (data, (LPCTSTR) _T("\\"), (void**) &info, &verSize)) + if (size > 0 && info != nullptr && info->dwSignature == 0xfeef04bd) + return (uint32) info->dwFileVersionMS; + } + } - return 0; -} + return 0; + } +#else + RTL_OSVERSIONINFOW getWindowsVersionInfo() + { + RTL_OSVERSIONINFOW versionInfo = { 0 }; + + if (auto* moduleHandle = ::GetModuleHandleW (L"ntdll.dll")) + { + using RtlGetVersion = LONG (WINAPI*) (PRTL_OSVERSIONINFOW); + + if (auto* rtlGetVersion = (RtlGetVersion) ::GetProcAddress (moduleHandle, "RtlGetVersion")) + { + versionInfo.dwOSVersionInfoSize = sizeof (versionInfo); + LONG STATUS_SUCCESS = 0; + + if (rtlGetVersion (&versionInfo) != STATUS_SUCCESS) + versionInfo = { 0 }; + } + } + + return versionInfo; + } +#endif SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() { + #if JUCE_MINGW auto v = getWindowsVersion(); - auto major = (v >> 16); + auto major = (v >> 16) & 0xff; + auto minor = (v >> 0) & 0xff; + #else + auto versionInfo = getWindowsVersionInfo(); + auto major = versionInfo.dwMajorVersion; + auto minor = versionInfo.dwMinorVersion; + #endif jassert (major <= 10); // need to add support for new version! - if (major == 10) return Windows10; - if (v == 0x00060003) return Windows8_1; - if (v == 0x00060002) return Windows8_0; - if (v == 0x00060001) return Windows7; - if (v == 0x00060000) return WinVista; - if (v == 0x00050000) return Win2000; - if (major == 5) return WinXP; + if (major == 10) return Windows10; + if (major == 6 && minor == 3) return Windows8_1; + if (major == 6 && minor == 2) return Windows8_0; + if (major == 6 && minor == 1) return Windows7; + if (major == 6 && minor == 0) return WinVista; + if (major == 5 && minor == 1) return WinXP; + if (major == 5 && minor == 0) return Win2000; jassertfalse; return UnknownOS; diff --git a/JuceLibraryCode/modules/juce_core/native/juce_win32_Threads.cpp b/JuceLibraryCode/modules/juce_core/native/juce_win32_Threads.cpp index aa826d0..749bf74 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_win32_Threads.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_win32_Threads.cpp @@ -48,20 +48,6 @@ bool CriticalSection::tryEnter() const noexcept { return TryEnterCriticalSec void CriticalSection::exit() const noexcept { LeaveCriticalSection ((CRITICAL_SECTION*) lock); } -//============================================================================== -WaitableEvent::WaitableEvent (const bool manualReset) noexcept - : handle (CreateEvent (0, manualReset ? TRUE : FALSE, FALSE, 0)) {} - -WaitableEvent::~WaitableEvent() noexcept { CloseHandle (handle); } - -void WaitableEvent::signal() const noexcept { SetEvent (handle); } -void WaitableEvent::reset() const noexcept { ResetEvent (handle); } - -bool WaitableEvent::wait (const int timeOutMs) const noexcept -{ - return WaitForSingleObject (handle, (DWORD) timeOutMs) == WAIT_OBJECT_0; -} - //============================================================================== void JUCE_API juce_threadEntryPoint (void*); @@ -258,15 +244,8 @@ void JUCE_CALLTYPE Process::setCurrentModuleInstanceHandle (void* const newHandl currentModuleHandle = newHandle; } -void JUCE_CALLTYPE Process::raisePrivilege() -{ - jassertfalse; // xxx not implemented -} - -void JUCE_CALLTYPE Process::lowerPrivilege() -{ - jassertfalse; // xxx not implemented -} +void JUCE_CALLTYPE Process::raisePrivilege() {} +void JUCE_CALLTYPE Process::lowerPrivilege() {} void JUCE_CALLTYPE Process::terminate() { diff --git a/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.cpp b/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.cpp index 78e8c08..35b3443 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.cpp +++ b/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.cpp @@ -372,15 +372,16 @@ Array IPAddress::getAllAddresses (bool includeIPv6) return addresses; } + +//============================================================================== //============================================================================== #if JUCE_UNIT_TESTS struct IPAddressTests : public UnitTest { IPAddressTests() - : UnitTest ("IPAddress", "Networking") - { - } + : UnitTest ("IPAddress", UnitTestCategories::networking) + {} void runTest() override { diff --git a/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.cpp b/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.cpp index 73649e3..4333b80 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.cpp +++ b/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.cpp @@ -60,8 +60,9 @@ String NamedPipe::getName() const // other methods for this class are implemented in the platform-specific files -//============================================================================== +//============================================================================== +//============================================================================== #if JUCE_UNIT_TESTS class NamedPipeTests : public UnitTest @@ -69,7 +70,7 @@ class NamedPipeTests : public UnitTest public: //============================================================================== NamedPipeTests() - : UnitTest ("NamedPipe", "Networking") + : UnitTest ("NamedPipe", UnitTestCategories::networking) {} void runTest() override @@ -197,9 +198,9 @@ private: //============================================================================== struct NamedPipeThread : public Thread { - NamedPipeThread (const String& threadName, const String& pName, + NamedPipeThread (const String& tName, const String& pName, bool shouldCreatePipe, WaitableEvent& completed) - : Thread (threadName), pipeName (pName), workCompleted (completed) + : Thread (tName), pipeName (pName), workCompleted (completed) { if (shouldCreatePipe) pipe.createNewPipe (pipeName); @@ -207,6 +208,11 @@ private: pipe.openExisting (pipeName); } + ~NamedPipeThread() + { + stopThread (100); + } + NamedPipe pipe; const String& pipeName; WaitableEvent& workCompleted; diff --git a/JuceLibraryCode/modules/juce_core/network/juce_Socket.cpp b/JuceLibraryCode/modules/juce_core/network/juce_Socket.cpp index 8196391..1e28be5 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_Socket.cpp +++ b/JuceLibraryCode/modules/juce_core/network/juce_Socket.cpp @@ -33,19 +33,19 @@ namespace juce #endif #if JUCE_WINDOWS - typedef int juce_socklen_t; - typedef int juce_recvsend_size_t; - typedef SOCKET SocketHandle; + using juce_socklen_t = int; + using juce_recvsend_size_t = int; + using SocketHandle = SOCKET; static const SocketHandle invalidSocket = INVALID_SOCKET; #elif JUCE_ANDROID - typedef socklen_t juce_socklen_t; - typedef size_t juce_recvsend_size_t; - typedef int SocketHandle; + using juce_socklen_t = socklen_t; + using juce_recvsend_size_t = size_t; + using SocketHandle = int; static const SocketHandle invalidSocket = -1; #else - typedef socklen_t juce_socklen_t; - typedef socklen_t juce_recvsend_size_t; - typedef int SocketHandle; + using juce_socklen_t = socklen_t; + using juce_recvsend_size_t = socklen_t; + using SocketHandle = int; static const SocketHandle invalidSocket = -1; #endif @@ -87,7 +87,7 @@ namespace SocketHelpers static bool resetSocketOptions (SocketHandle handle, bool isDatagram, bool allowBroadcast) noexcept { - return handle > 0 + return handle != invalidSocket && setOption (handle, SO_RCVBUF, (int) 65536) && setOption (handle, SO_SNDBUF, (int) 65536) && (isDatagram ? ((! allowBroadcast) || setOption (handle, SO_BROADCAST, (int) 1)) @@ -103,7 +103,7 @@ namespace SocketHelpers #if JUCE_WINDOWS ignoreUnused (portNumber, isListener, readLock); - if (h != (unsigned) SOCKET_ERROR || connected) + if (h != invalidSocket || connected) closesocket (h); // make sure any read process finishes before we delete the socket @@ -122,7 +122,7 @@ namespace SocketHelpers } } - if (h != -1) + if (h >= 0) { // unblock any pending read requests ::shutdown (h, SHUT_RDWR); @@ -147,7 +147,7 @@ namespace SocketHelpers static bool bindSocket (SocketHandle handle, int port, const String& address) noexcept { - if (handle <= 0 || ! isValidPortNumber (port)) + if (handle == invalidSocket || ! isValidPortNumber (port)) return false; struct sockaddr_in addr; @@ -163,7 +163,7 @@ namespace SocketHelpers static int getBoundPort (SocketHandle handle) noexcept { - if (handle > 0) + if (handle != invalidSocket) { struct sockaddr_in addr; socklen_t len = sizeof (addr); @@ -183,9 +183,36 @@ namespace SocketHelpers if (getpeername (handle, (struct sockaddr*) &addr, &len) >= 0) return inet_ntoa (addr.sin_addr); - return String ("0.0.0.0"); + return "0.0.0.0"; } + static bool setSocketBlockingState (SocketHandle handle, bool shouldBlock) noexcept + { + #if JUCE_WINDOWS + u_long nonBlocking = shouldBlock ? 0 : (u_long) 1; + return ioctlsocket (handle, FIONBIO, &nonBlocking) == 0; + #else + int socketFlags = fcntl (handle, F_GETFL, 0); + + if (socketFlags == -1) + return false; + + if (shouldBlock) + socketFlags &= ~O_NONBLOCK; + else + socketFlags |= O_NONBLOCK; + + return fcntl (handle, F_SETFL, socketFlags) == 0; + #endif + } + + #if ! JUCE_WINDOWS + static bool getSocketBlockingState (SocketHandle handle) + { + return (fcntl (handle, F_GETFL, 0) & O_NONBLOCK) == 0; + } + #endif + static int readSocket (SocketHandle handle, void* destBuffer, int maxBytesToRead, std::atomic& connected, @@ -194,6 +221,11 @@ namespace SocketHelpers String* senderIP = nullptr, int* senderPort = nullptr) noexcept { + #if ! JUCE_WINDOWS + if (blockUntilSpecifiedAmountHasArrived != getSocketBlockingState (handle)) + #endif + setSocketBlockingState (handle, blockUntilSpecifiedAmountHasArrived); + int bytesRead = 0; while (bytesRead < maxBytesToRead) @@ -251,8 +283,25 @@ namespace SocketHelpers if (! lock.isLocked()) return -1; - int h = handle.load(); + auto hasErrorOccurred = [&handle] () -> bool + { + auto h = handle.load(); + if (h == invalidSocket) + return true; + + int opt; + juce_socklen_t len = sizeof (opt); + + if (getsockopt (h, SOL_SOCKET, SO_ERROR, (char*) &opt, &len) < 0 || opt != 0) + return true; + + return false; + }; + + auto h = handle.load(); + + #if JUCE_WINDOWS || JUCE_MINGW struct timeval timeout; struct timeval* timeoutp; @@ -273,60 +322,33 @@ namespace SocketHelpers FD_ZERO (&wset); FD_SET (h, &wset); - fd_set* const prset = forReading ? &rset : nullptr; - fd_set* const pwset = forReading ? nullptr : &wset; + fd_set* prset = forReading ? &rset : nullptr; + fd_set* pwset = forReading ? nullptr : &wset; - #if JUCE_WINDOWS - if (select ((int) h + 1, prset, pwset, 0, timeoutp) < 0) + // NB - need to use select() here as WSAPoll is broken on Windows + if (select ((int) h + 1, prset, pwset, nullptr, timeoutp) < 0 || hasErrorOccurred()) return -1; - #else - { - int result; - - while ((result = select (h + 1, prset, pwset, nullptr, timeoutp)) < 0 - && errno == EINTR) - { - } - - if (result < 0) - return -1; - } - #endif - - // we are closing - if (handle.load() < 0) - return -1; - - { - int opt; - juce_socklen_t len = sizeof (opt); - - if (getsockopt (h, SOL_SOCKET, SO_ERROR, (char*) &opt, &len) < 0 - || opt != 0) - return -1; - } return FD_ISSET (h, forReading ? &rset : &wset) ? 1 : 0; - } + #else + short eventsFlag = (forReading ? POLLIN : POLLOUT); + pollfd pfd { (SocketHandle) h, eventsFlag, 0 }; - static bool setSocketBlockingState (SocketHandle handle, bool shouldBlock) noexcept - { - #if JUCE_WINDOWS - u_long nonBlocking = shouldBlock ? 0 : (u_long) 1; - return ioctlsocket (handle, FIONBIO, &nonBlocking) == 0; - #else - int socketFlags = fcntl (handle, F_GETFL, 0); + int result = 0; - if (socketFlags == -1) - return false; + for (;;) + { + result = poll (&pfd, 1, timeoutMsecs); - if (shouldBlock) - socketFlags &= ~O_NONBLOCK; - else - socketFlags |= O_NONBLOCK; + if (result >= 0 || errno != EINTR) + break; + } - return fcntl (handle, F_SETFL, socketFlags) == 0; - #endif + if (result < 0 || hasErrorOccurred()) + return -1; + + return (pfd.revents & eventsFlag) != 0; + #endif } static addrinfo* getAddressInfo (bool isDatagram, const String& hostName, int portNumber) @@ -501,7 +523,8 @@ bool StreamingSocket::connect (const String& remoteHostName, int remotePortNumbe if (isListener) { - jassertfalse; // a listener socket can't connect to another one! + // a listener socket can't connect to another one! + jassertfalse; return false; } @@ -515,7 +538,10 @@ bool StreamingSocket::connect (const String& remoteHostName, int remotePortNumbe connected = SocketHelpers::connectSocket (handle, readLock, remoteHostName, remotePortNumber, timeOutMillisecs); - if (! (connected && SocketHelpers::resetSocketOptions (handle, false, false))) + if (! connected) + return false; + + if (! SocketHelpers::resetSocketOptions (handle, false, false)) { close(); return false; @@ -526,7 +552,8 @@ bool StreamingSocket::connect (const String& remoteHostName, int remotePortNumbe void StreamingSocket::close() { - SocketHelpers::closeSocket (handle, readLock, isListener, portNumber, connected); + if (handle >= 0) + SocketHelpers::closeSocket (handle, readLock, isListener, portNumber, connected); hostName.clear(); portNumber = 0; @@ -631,8 +658,11 @@ void DatagramSocket::shutdown() std::atomic handleCopy { handle.load() }; handle = -1; + std::atomic connected { false }; SocketHelpers::closeSocket (handleCopy, readLock, false, 0, connected); + + isBound = false; } bool DatagramSocket::bindToPort (int port) @@ -644,6 +674,9 @@ bool DatagramSocket::bindToPort (int port, const String& addr) { jassert (SocketHelpers::isValidPortNumber (port)); + if (handle < 0) + return false; + if (SocketHelpers::bindSocket (handle, port, addr)) { isBound = true; @@ -674,8 +707,6 @@ int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock return -1; std::atomic connected { true }; - - SocketHelpers::setSocketBlockingState (handle, shouldBlock); return SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, connected, shouldBlock, readLock); } @@ -686,8 +717,6 @@ int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock return -1; std::atomic connected { true }; - - SocketHelpers::setSocketBlockingState (handle, shouldBlock); return SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, connected, shouldBlock, readLock, &senderIPAddress, &senderPort); } @@ -722,7 +751,7 @@ int DatagramSocket::write (const String& remoteHostname, int remotePortNumber, bool DatagramSocket::joinMulticast (const String& multicastIPAddress) { - if (! isBound || handle < 0) + if (handle < 0 || ! isBound) return false; return SocketHelpers::multicast (handle, multicastIPAddress, lastBindAddress, true); @@ -730,7 +759,7 @@ bool DatagramSocket::joinMulticast (const String& multicastIPAddress) bool DatagramSocket::leaveMulticast (const String& multicastIPAddress) { - if (! isBound || handle < 0) + if (handle < 0 || ! isBound) return false; return SocketHelpers::multicast (handle, multicastIPAddress, lastBindAddress, false); @@ -738,7 +767,7 @@ bool DatagramSocket::leaveMulticast (const String& multicastIPAddress) bool DatagramSocket::setMulticastLoopbackEnabled (bool enable) { - if (! isBound || handle < 0) + if (handle < 0 || ! isBound) return false; return SocketHelpers::setOption (handle, IPPROTO_IP, IP_MULTICAST_LOOP, enable); @@ -766,4 +795,73 @@ bool DatagramSocket::setEnablePortReuse (bool enabled) #pragma warning (pop) #endif + +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +struct SocketTests : public UnitTest +{ + SocketTests() + : UnitTest ("Sockets", UnitTestCategories::networking) + { + } + + void runTest() override + { + auto localHost = IPAddress::local(); + int portNum = 12345; + + beginTest ("StreamingSocket"); + { + StreamingSocket socketServer; + + expect (socketServer.isConnected() == false); + expect (socketServer.getHostName().isEmpty()); + expect (socketServer.getBoundPort() == -1); + expect (socketServer.getRawSocketHandle() == invalidSocket); + + expect (socketServer.createListener (portNum, localHost.toString())); + + StreamingSocket socket; + + expect (socket.connect (localHost.toString(), portNum)); + + expect (socket.isConnected() == true); + expect (socket.getHostName() == localHost.toString()); + expect (socket.getBoundPort() != -1); + expect (socket.getRawSocketHandle() != invalidSocket); + + socket.close(); + + expect (socket.isConnected() == false); + expect (socket.getHostName().isEmpty()); + expect (socket.getBoundPort() == -1); + expect (socket.getRawSocketHandle() == invalidSocket); + } + + beginTest ("DatagramSocket"); + { + DatagramSocket socket; + + expect (socket.getBoundPort() == -1); + expect (socket.getRawSocketHandle() != invalidSocket); + + expect (socket.bindToPort (portNum, localHost.toString())); + + expect (socket.getBoundPort() == portNum); + expect (socket.getRawSocketHandle() != invalidSocket); + + socket.shutdown(); + + expect (socket.getBoundPort() == -1); + expect (socket.getRawSocketHandle() == invalidSocket); + } + } +}; + +static SocketTests socketTests; + +#endif + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/network/juce_Socket.h b/JuceLibraryCode/modules/juce_core/network/juce_Socket.h index 524554e..2f30d0c 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_Socket.h +++ b/JuceLibraryCode/modules/juce_core/network/juce_Socket.h @@ -55,8 +55,8 @@ public: //============================================================================== /** Binds the socket to the specified local port. - @returns true on success; false may indicate that another socket is already bound - on the same port + @returns true on success; false may indicate that another socket is already bound + on the same port */ bool bindToPort (int localPortNumber); @@ -66,8 +66,9 @@ public: as well. This is useful if you would like to bind your socket to a specific network adapter. Note that localAddress must be an IP address assigned to one of your network address otherwise this function will fail. - @returns true on success; false may indicate that another socket is already bound - on the same port + + @returns true on success; false may indicate that another socket is already bound + on the same port @see bindToPort(int localPortNumber), IPAddress::getAllAddresses */ bool bindToPort (int localPortNumber, const String& localAddress); @@ -76,7 +77,9 @@ public: This is useful if you need to know to which port the OS has actually bound your socket when calling the constructor or bindToPort with zero as the - localPortNumber argument. Returns -1 if the function fails. + localPortNumber argument. + + @returns -1 if the function fails */ int getBoundPort() const noexcept; @@ -85,7 +88,7 @@ public: If timeOutMillisecs is 0, then this method will block until the operating system rejects the connection (which could take a long time). - @returns true if it succeeds. + @returns true if it succeeds, false if otherwise @see isConnected */ bool connect (const String& remoteHostname, @@ -119,8 +122,8 @@ public: If the timeout is < 0, it will wait forever, or else will give up after the specified time. - If the socket is ready on return, this returns 1. If it times-out before - the socket becomes ready, it returns 0. If an error occurs, it returns -1. + @returns 1 if the socket is ready on return, 0 if it times-out before + the socket becomes ready, or -1 if an error occurs */ int waitUntilReady (bool readyForReading, int timeoutMsecs); @@ -131,7 +134,7 @@ public: flag is false, the method will return as much data as is currently available without blocking. - @returns the number of bytes read, or -1 if there was an error. + @returns the number of bytes read, or -1 if there was an error @see waitUntilReady */ int read (void* destBuffer, int maxBytesToRead, @@ -142,7 +145,7 @@ public: Note that this method will block unless you have checked the socket is ready for writing before calling it (see the waitUntilReady() method). - @returns the number of bytes written, or -1 if there was an error. + @returns the number of bytes written, or -1 if there was an error */ int write (const void* sourceBuffer, int numBytesToWrite); @@ -156,8 +159,8 @@ public: @param portNumber the port number to listen on @param localHostName the interface address to listen on - pass an empty string to listen on all addresses - @returns true if it manages to open the socket successfully. + @returns true if it manages to open the socket successfully @see waitForNextConnection */ bool createListener (int portNumber, const String& localHostName = String()); @@ -177,8 +180,7 @@ private: //============================================================================== String hostName; std::atomic portNumber { 0 }, handle { -1 }; - std::atomic connected { false }; - bool isListener = false; + std::atomic connected { false }, isListener { false }; mutable CriticalSection readLock; StreamingSocket (const String& hostname, int portNumber, int handle); @@ -202,8 +204,7 @@ class JUCE_API DatagramSocket final { public: //============================================================================== - /** - Creates a datagram socket. + /** Creates a datagram socket. You first need to bind this socket to a port with bindToPort if you intend to read from this socket. @@ -223,8 +224,8 @@ public: The localPortNumber is the port on which to bind this socket. If this value is 0, the port number is assigned by the operating system. - @returns true on success; false may indicate that another socket is already bound - on the same port + @returns true on success; false may indicate that another socket is already bound + on the same port */ bool bindToPort (int localPortNumber); @@ -234,8 +235,9 @@ public: as well. This is useful if you would like to bind your socket to a specific network adapter. Note that localAddress must be an IP address assigned to one of your network address otherwise this function will fail. - @returns true on success; false may indicate that another socket is already bound - on the same port + + @returns true on success; false may indicate that another socket is already bound + on the same port @see bindToPort(int localPortNumber), IPAddress::getAllAddresses */ bool bindToPort (int localPortNumber, const String& localAddress); @@ -245,7 +247,8 @@ public: This is useful if you need to know to which port the OS has actually bound your socket when bindToPort was called with zero. - Returns -1 if the socket didn't bind to any port yet or an error occurred. */ + @returns -1 if the socket didn't bind to any port yet or an error occurred + */ int getBoundPort() const noexcept; /** Returns the OS's socket handle that's currently open. */ @@ -260,8 +263,8 @@ public: If the timeout is < 0, it will wait forever, or else will give up after the specified time. - If the socket is ready on return, this returns 1. If it times-out before - the socket becomes ready, it returns 0. If an error occurs, it returns -1. + @returns 1 if the socket is ready on return, 0 if it times-out before the + socket becomes ready, or -1 if an error occurs */ int waitUntilReady (bool readyForReading, int timeoutMsecs); @@ -272,7 +275,7 @@ public: flag is false, the method will return as much data as is currently available without blocking. - @returns the number of bytes read, or -1 if there was an error. + @returns the number of bytes read, or -1 if there was an error @see waitUntilReady */ int read (void* destBuffer, int maxBytesToRead, @@ -285,8 +288,8 @@ public: flag is false, the method will return as much data as is currently available without blocking. - @returns the number of bytes read, or -1 if there was an error. On a successful - result, the senderIPAddress value will be set to the IP of the sender. + @returns the number of bytes read, or -1 if there was an error. On a successful + result, the senderIPAddress value will be set to the IP of the sender @see waitUntilReady */ int read (void* destBuffer, int maxBytesToRead, @@ -298,7 +301,7 @@ public: Note that this method will block unless you have checked the socket is ready for writing before calling it (see the waitUntilReady() method). - @returns the number of bytes written, or -1 if there was an error. + @returns the number of bytes written, or -1 if there was an error */ int write (const String& remoteHostname, int remotePortNumber, const void* sourceBuffer, int numBytesToWrite); @@ -306,8 +309,10 @@ public: /** Closes the underlying socket object. Closes the underlying socket object and aborts any read or write operations. - Note that all other methods will return an error after this call. This - method is useful if another thread is blocking in a read/write call and you + Note that all other methods will return an error after this call and the object + cannot be re-used. + + This method is useful if another thread is blocking in a read/write call and you would like to abort the read/write thread. Simply deleting the socket object without calling shutdown may cause a race-condition where the read/write returns just before the socket is deleted and the subsequent read/write would @@ -319,17 +324,20 @@ public: //============================================================================== /** Join a multicast group. - @returns true if it succeeds. + + @returns true if it succeeds */ bool joinMulticast (const String& multicastIPAddress); /** Leave a multicast group. - @returns true if it succeeds. + + @returns true if it succeeds */ bool leaveMulticast (const String& multicastIPAddress); /** Enables or disables multicast loopback. - @returns true if it succeeds. + + @returns true if it succeeds */ bool setMulticastLoopbackEnabled (bool enableLoopback); @@ -340,7 +348,7 @@ public: Do not use this if your socket handles sensitive data as it could be read by any, possibly malicious, third-party apps. - Returns true on success. + @returns true on success */ bool setEnablePortReuse (bool enabled); diff --git a/JuceLibraryCode/modules/juce_core/network/juce_URL.cpp b/JuceLibraryCode/modules/juce_core/network/juce_URL.cpp index a130bae..56f2a3c 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_URL.cpp +++ b/JuceLibraryCode/modules/juce_core/network/juce_URL.cpp @@ -62,8 +62,8 @@ struct FallbackDownloadTask : public URL::DownloadTask, if (listener != nullptr) listener->progress (this, downloaded, contentLength); - auto max = jmin ((int) bufferSize, contentLength < 0 ? std::numeric_limits::max() - : static_cast (contentLength - downloaded)); + auto max = (int) jmin ((int64) bufferSize, contentLength < 0 ? std::numeric_limits::max() + : static_cast (contentLength - downloaded)); auto actual = stream->read (buffer.get(), max); @@ -82,7 +82,7 @@ struct FallbackDownloadTask : public URL::DownloadTask, break; } - fileStream->flush(); + fileStream.reset(); if (threadShouldExit() || stream->isError()) error = true; @@ -97,7 +97,7 @@ struct FallbackDownloadTask : public URL::DownloadTask, } //============================================================================== - const std::unique_ptr fileStream; + std::unique_ptr fileStream; const std::unique_ptr stream; const size_t bufferSize; HeapBlock buffer; @@ -135,7 +135,7 @@ URL::DownloadTask::DownloadTask() {} URL::DownloadTask::~DownloadTask() {} //============================================================================== -URL::URL() noexcept {} +URL::URL() {} URL::URL (const String& u) : url (u) { @@ -171,7 +171,6 @@ URL::URL (File localFile) url = "/" + url; } - url = "file://" + url; jassert (isWellFormed()); @@ -209,34 +208,6 @@ void URL::init() URL::URL (const String& u, int) : url (u) {} -URL::URL (URL&& other) - : url (std::move (other.url)), - postData (std::move (other.postData)), - parameterNames (std::move (other.parameterNames)), - parameterValues (std::move (other.parameterValues)), - filesToUpload (std::move (other.filesToUpload)) - #if JUCE_IOS - , bookmark (std::move (other.bookmark)) - #endif -{ -} - -URL& URL::operator= (URL&& other) -{ - url = std::move (other.url); - postData = std::move (other.postData); - parameterNames = std::move (other.parameterNames); - parameterValues = std::move (other.parameterValues); - filesToUpload = std::move (other.filesToUpload); - #if JUCE_IOS - bookmark = std::move (other.bookmark); - #endif - - return *this; -} - -URL::~URL() {} - URL URL::createWithoutParsing (const String& u) { return URL (u, 0); @@ -315,6 +286,20 @@ namespace URLHelpers else path += suffix; } + + static String removeLastPathSection (const String& url) + { + auto startOfPath = findStartOfPath (url); + auto lastSlash = url.lastIndexOfChar ('/'); + + if (lastSlash > startOfPath && lastSlash == url.length() - 1) + return removeLastPathSection (url.dropLastCharacters (1)); + + if (lastSlash < 0) + return url; + + return url.substring (0, std::max (startOfPath, lastSlash)); + } } void URL::addParameter (const String& name, const String& value) @@ -325,8 +310,8 @@ void URL::addParameter (const String& name, const String& value) String URL::toString (bool includeGetParameters) const { - if (includeGetParameters && parameterNames.size() > 0) - return url + "?" + URLHelpers::getMangledParameters (*this); + if (includeGetParameters) + return url + getQueryString(); return url; } @@ -347,12 +332,24 @@ String URL::getDomain() const return getDomainInternal (false); } -String URL::getSubPath() const +String URL::getSubPath (bool includeGetParameters) const { auto startOfPath = URLHelpers::findStartOfPath (url); + auto subPath = startOfPath <= 0 ? String() + : url.substring (startOfPath); - return startOfPath <= 0 ? String() - : url.substring (startOfPath); + if (includeGetParameters) + subPath += getQueryString(); + + return subPath; +} + +String URL::getQueryString() const +{ + if (parameterNames.size() > 0) + return "?" + URLHelpers::getMangledParameters (*this); + + return {}; } String URL::getScheme() const @@ -360,10 +357,10 @@ String URL::getScheme() const return url.substring (0, URLHelpers::findEndOfScheme (url) - 1); } -#ifndef JUCE_ANDROID +#if ! JUCE_ANDROID bool URL::isLocalFile() const { - return (getScheme() == "file"); + return getScheme() == "file"; } File URL::getLocalFile() const @@ -387,7 +384,7 @@ File URL::fileFromFileSchemeURL (const URL& fileURL) auto path = removeEscapeChars (fileURL.getDomainInternal (true)).replace ("+", "%2B"); - #ifdef JUCE_WINDOWS + #if JUCE_WINDOWS bool isUncPath = (! fileURL.url.startsWith ("file:///")); #else path = File::getSeparatorString() + path; @@ -398,7 +395,7 @@ File URL::fileFromFileSchemeURL (const URL& fileURL) for (auto urlElement : urlElements) path += File::getSeparatorString() + removeEscapeChars (urlElement.replace ("+", "%2B")); - #ifdef JUCE_WINDOWS + #if JUCE_WINDOWS if (isUncPath) path = "\\\\" + path; #endif @@ -422,10 +419,10 @@ URL URL::withNewDomainAndPath (const String& newURL) const URL URL::withNewSubPath (const String& newPath) const { - const int startOfPath = URLHelpers::findStartOfPath (url); - URL u (*this); + auto startOfPath = URLHelpers::findStartOfPath (url); + if (startOfPath > 0) u.url = url.substring (0, startOfPath); @@ -433,6 +430,13 @@ URL URL::withNewSubPath (const String& newPath) const return u; } +URL URL::getParentURL() const +{ + URL u (*this); + u.url = URLHelpers::removeLastPathSection (u.url); + return u; +} + URL URL::getChildURL (const String& subPath) const { URL u (*this); @@ -498,18 +502,15 @@ void URL::createHeadersAndPostData (String& headers, MemoryBlock& postDataToWrit //============================================================================== bool URL::isProbablyAWebsiteURL (const String& possibleURL) { - static const char* validProtocols[] = { "http:", "ftp:", "https:" }; - - for (auto* protocol : validProtocols) + for (auto* protocol : { "http:", "https:", "ftp:" }) if (possibleURL.startsWithIgnoreCase (protocol)) return true; - if (possibleURL.containsChar ('@') - || possibleURL.containsChar (' ')) + if (possibleURL.containsChar ('@') || possibleURL.containsChar (' ')) return false; - const String topLevelDomain (possibleURL.upToFirstOccurrenceOf ("/", false, false) - .fromLastOccurrenceOf (".", false, false)); + auto topLevelDomain = possibleURL.upToFirstOccurrenceOf ("/", false, false) + .fromLastOccurrenceOf (".", false, false); return topLevelDomain.isNotEmpty() && topLevelDomain.length() <= 3; } @@ -536,8 +537,7 @@ String URL::getDomainInternal (bool ignorePort) const } #if JUCE_IOS -URL::Bookmark::Bookmark (void* bookmarkToUse) - : data (bookmarkToUse) +URL::Bookmark::Bookmark (void* bookmarkToUse) : data (bookmarkToUse) { } @@ -628,12 +628,10 @@ private: return urlToUse.getLocalFile(); } - else - { - auto desc = [error localizedDescription]; - ignoreUnused (desc); - jassertfalse; - } + + auto desc = [error localizedDescription]; + ignoreUnused (desc); + jassertfalse; } return urlToUse.getLocalFile(); @@ -675,10 +673,9 @@ InputStream* URL::createInputStream (bool usePostCommand, #else return getLocalFile().createInputStream(); #endif - } - std::unique_ptr wi (new WebInputStream (*this, usePostCommand)); + auto wi = std::make_unique (*this, usePostCommand); struct ProgressCallbackCaller : public WebInputStream::Listener { @@ -693,10 +690,6 @@ InputStream* URL::createInputStream (bool usePostCommand, OpenStreamProgressCallback* callback; void* const data; - - // workaround a MSVC 2013 compiler warning - ProgressCallbackCaller (const ProgressCallbackCaller& o) : callback (o.callback), data (o.data) { jassertfalse; } - ProgressCallbackCaller& operator= (const ProgressCallbackCaller&) { jassertfalse; return *this; } }; std::unique_ptr callbackCaller @@ -754,7 +747,7 @@ OutputStream* URL::createOutputStream() const bool URL::readEntireBinaryStream (MemoryBlock& destData, bool usePostCommand) const { const std::unique_ptr in (isLocalFile() ? getLocalFile().createInputStream() - : static_cast (createInputStream (usePostCommand))); + : createInputStream (usePostCommand)); if (in != nullptr) { @@ -768,7 +761,7 @@ bool URL::readEntireBinaryStream (MemoryBlock& destData, bool usePostCommand) co String URL::readEntireTextStream (bool usePostCommand) const { const std::unique_ptr in (isLocalFile() ? getLocalFile().createInputStream() - : static_cast (createInputStream (usePostCommand))); + : createInputStream (usePostCommand)); if (in != nullptr) return in->readEntireStreamAsString(); @@ -776,23 +769,23 @@ String URL::readEntireTextStream (bool usePostCommand) const return {}; } -XmlElement* URL::readEntireXmlStream (bool usePostCommand) const +std::unique_ptr URL::readEntireXmlStream (bool usePostCommand) const { - return XmlDocument::parse (readEntireTextStream (usePostCommand)); + return parseXML (readEntireTextStream (usePostCommand)); } //============================================================================== URL URL::withParameter (const String& parameterName, const String& parameterValue) const { - URL u (*this); + auto u = *this; u.addParameter (parameterName, parameterValue); return u; } URL URL::withParameters (const StringPairArray& parametersToAdd) const { - URL u (*this); + auto u = *this; for (int i = 0; i < parametersToAdd.size(); ++i) u.addParameter (parametersToAdd.getAllKeys()[i], @@ -808,7 +801,7 @@ URL URL::withPOSTData (const String& newPostData) const URL URL::withPOSTData (const MemoryBlock& newPostData) const { - URL u (*this); + auto u = *this; u.postData = newPostData; return u; } @@ -822,7 +815,7 @@ URL::Upload::Upload (const String& param, const String& name, URL URL::withUpload (Upload* const f) const { - URL u (*this); + auto u = *this; for (int i = u.filesToUpload.size(); --i >= 0;) if (u.filesToUpload.getObjectPointerUnchecked(i)->parameterName == f->parameterName) diff --git a/JuceLibraryCode/modules/juce_core/network/juce_URL.h b/JuceLibraryCode/modules/juce_core/network/juce_URL.h index 81724f9..6e60882 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_URL.h +++ b/JuceLibraryCode/modules/juce_core/network/juce_URL.h @@ -39,7 +39,7 @@ class JUCE_API URL public: //============================================================================== /** Creates an empty URL. */ - URL() noexcept; + URL(); /** Creates a URL from a string. This will parse any embedded parameters after a '?' character and store them @@ -50,16 +50,14 @@ public: URL (const URL&) = default; URL& operator= (const URL&) = default; - - // VS2013 can't default move constructors and assignments - URL (URL&&); - URL& operator= (URL&&); + URL (URL&&) = default; + URL& operator= (URL&&) = default; /** Creates URL referring to a local file on your disk using the file:// scheme. */ explicit URL (File); /** Destructor. */ - ~URL(); + ~URL() = default; /** Compares two URLs. All aspects of the URLs must be identical for them to match, including any parameters, @@ -90,8 +88,17 @@ public: /** Returns the path part of the URL. E.g. for "http://www.xyz.com/foo/bar?x=1", this will return "foo/bar". + + If includeGetParameters is true and any parameters have been set with the + withParameter() method, then the string will have these appended on the + end and url-encoded. */ - String getSubPath() const; + String getSubPath (bool includeGetParameters = false) const; + + /** If any parameters are set, returns these URL encoded, including the "?" + * prefix. + */ + String getQueryString() const; /** Returns the scheme of the URL. E.g. for "http://www.xyz.com/foobar", this will return "http". (It won't @@ -141,6 +148,11 @@ public: */ URL withNewSubPath (const String& newPath) const; + /** Attempts to return a URL which is the parent folder containing this URL. + If there isn't a parent, this method will just return a copy of this URL. + */ + URL getParentURL() const; + /** Returns a new URL that refers to a sub-path relative to this one. E.g. if the URL is "http://www.xyz.com/foo" and you call this with "bar", it'll return "http://www.xyz.com/foo/bar". Note that there's no way for @@ -256,7 +268,7 @@ public: URL withPOSTData (const MemoryBlock& postData) const; /** Returns the data that was set using withPOSTData(). */ - String getPostData() const noexcept { return postData.toString(); } + String getPostData() const { return postData.toString(); } /** Returns the data that was set using withPOSTData() as MemoryBlock. */ const MemoryBlock& getPostDataAsMemoryBlock() const noexcept { return postData; } @@ -331,12 +343,12 @@ public: InputStream* createInputStream (bool doPostLikeRequest, OpenStreamProgressCallback* progressCallback = nullptr, void* progressCallbackContext = nullptr, - String extraHeaders = String(), + String extraHeaders = {}, int connectionTimeOutMs = 0, StringPairArray* responseHeaders = nullptr, int* statusCode = nullptr, int numRedirectsToFollow = 5, - String httpRequestCmd = String()) const; + String httpRequestCmd = {}) const; /** Attempts to open an output stream to a URL for writing @@ -478,7 +490,7 @@ public: @see readEntireBinaryStream, readEntireTextStream */ - XmlElement* readEntireXmlStream (bool usePostCommand = false) const; + std::unique_ptr readEntireXmlStream (bool usePostCommand = false) const; //============================================================================== /** Adds escape sequences to a string to encode any characters that aren't diff --git a/JuceLibraryCode/modules/juce_core/network/juce_WebInputStream.h b/JuceLibraryCode/modules/juce_core/network/juce_WebInputStream.h index 18602e7..b4a8194 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_WebInputStream.h +++ b/JuceLibraryCode/modules/juce_core/network/juce_WebInputStream.h @@ -190,10 +190,10 @@ class JUCE_API WebInputStream : public InputStream The position is an absolute number of bytes from the stream's start. For a WebInputStream, this method will fail if wantedPos is smaller - than the curent position. If wantedPos is greater than the current + than the current position. If wantedPos is greater than the current position, then calling setPosition is the same as calling read, i.e. the skipped data will still be downloaded, although skipped bytes will - be discarded immedietely. + be discarded immediately. @returns true if the stream manages to reposition itself correctly @see getPosition diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.cpp b/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.cpp index 546b275..5c52533 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.cpp @@ -196,13 +196,15 @@ String BufferedInputStream::readString() return InputStream::readString(); } + +//============================================================================== //============================================================================== #if JUCE_UNIT_TESTS struct BufferedInputStreamTests : public UnitTest { BufferedInputStreamTests() - : UnitTest ("BufferedInputStream", "Streams") + : UnitTest ("BufferedInputStream", UnitTestCategories::streams) {} void runTest() override diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.cpp b/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.cpp index b63ac69..79c66ac 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.cpp @@ -33,6 +33,26 @@ int64 InputStream::getNumBytesRemaining() return len; } +ssize_t InputStream::read (void* destBuffer, size_t size) +{ + ssize_t totalRead = 0; + + while (size > 0) + { + auto numToRead = (int) std::min (size, (size_t) 0x70000000); + auto numRead = read (juce::addBytesToPointer (destBuffer, totalRead), numToRead); + jassert (numRead <= numToRead); + + if (numRead < 0) return (ssize_t) numRead; + if (numRead == 0) break; + + size -= (size_t) numRead; + totalRead += numRead; + } + + return totalRead; +} + char InputStream::readByte() { char temp = 0; diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.h b/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.h index 38d5863..8d77479 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.h +++ b/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.h @@ -77,6 +77,8 @@ public: */ virtual int read (void* destBuffer, int maxBytesToRead) = 0; + ssize_t read (void* destBuffer, size_t maxBytesToRead); + /** Reads a byte from the stream. If the stream is exhausted, this will return zero. @see OutputStream::writeByte diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.cpp b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.cpp index 12a42ff..cd7aa09 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.cpp @@ -28,7 +28,10 @@ MemoryInputStream::MemoryInputStream (const void* sourceData, size_t sourceDataS dataSize (sourceDataSize) { if (keepCopy) - createInternalCopy(); + { + internalCopy = MemoryBlock (sourceData, sourceDataSize); + data = internalCopy.getData(); + } } MemoryInputStream::MemoryInputStream (const MemoryBlock& sourceData, bool keepCopy) @@ -36,14 +39,16 @@ MemoryInputStream::MemoryInputStream (const MemoryBlock& sourceData, bool keepCo dataSize (sourceData.getSize()) { if (keepCopy) - createInternalCopy(); + { + internalCopy = sourceData; + data = internalCopy.getData(); + } } -void MemoryInputStream::createInternalCopy() +MemoryInputStream::MemoryInputStream (MemoryBlock&& source) + : internalCopy (std::move (source)) { - internalCopy.malloc (dataSize); - memcpy (internalCopy, data, dataSize); - data = internalCopy; + data = internalCopy.getData(); } MemoryInputStream::~MemoryInputStream() @@ -95,6 +100,8 @@ void MemoryInputStream::skipNextBytes (int64 numBytesToSkip) setPosition (getPosition() + numBytesToSkip); } + +//============================================================================== //============================================================================== #if JUCE_UNIT_TESTS @@ -102,7 +109,7 @@ class MemoryStreamTests : public UnitTest { public: MemoryStreamTests() - : UnitTest ("MemoryInputStream & MemoryOutputStream", "Streams") + : UnitTest ("MemoryInputStream & MemoryOutputStream", UnitTestCategories::streams) {} void runTest() override diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.h b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.h index fc08528..01aef62 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.h +++ b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.h @@ -62,6 +62,9 @@ public: MemoryInputStream (const MemoryBlock& data, bool keepInternalCopyOfData); + /** Creates a stream by moving from a MemoryBlock. */ + MemoryInputStream (MemoryBlock&& blockToTake); + /** Destructor. */ ~MemoryInputStream() override; @@ -83,9 +86,7 @@ private: //============================================================================== const void* data; size_t dataSize, position = 0; - HeapBlock internalCopy; - - void createInternalCopy(); + MemoryBlock internalCopy; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryInputStream) }; diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.h b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.h index 0a24803..231a7f5 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.h +++ b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.h @@ -45,7 +45,7 @@ public: Note that the destination block will always be larger than the amount of data that has been written to the stream, because the MemoryOutputStream keeps some - spare capactity at its end. To trim the block's size down to fit the actual + spare capacity at its end. To trim the block's size down to fit the actual data, call flush(), or delete the MemoryOutputStream. @param memoryBlockToWriteTo the block into which new data will be written. diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.cpp b/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.cpp index e3ab6a7..c2eb06f 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.cpp @@ -25,9 +25,10 @@ namespace juce #if JUCE_DEBUG +//============================================================================== struct DanglingStreamChecker { - DanglingStreamChecker() {} + DanglingStreamChecker() = default; ~DanglingStreamChecker() { @@ -38,12 +39,20 @@ struct DanglingStreamChecker nastiness.. */ jassert (activeStreams.size() == 0); + + // We need to flag when this helper struct has been destroyed to prevent some + // nasty order-of-static-destruction issues + hasBeenDestroyed = true; } Array activeStreams; + + static bool hasBeenDestroyed; }; +bool DanglingStreamChecker::hasBeenDestroyed = false; static DanglingStreamChecker danglingStreamChecker; + #endif //============================================================================== @@ -51,14 +60,16 @@ OutputStream::OutputStream() : newLineString (NewLine::getDefault()) { #if JUCE_DEBUG - danglingStreamChecker.activeStreams.add (this); + if (! DanglingStreamChecker::hasBeenDestroyed) + danglingStreamChecker.activeStreams.add (this); #endif } OutputStream::~OutputStream() { #if JUCE_DEBUG - danglingStreamChecker.activeStreams.removeFirstMatchingValue (this); + if (! DanglingStreamChecker::hasBeenDestroyed) + danglingStreamChecker.activeStreams.removeFirstMatchingValue (this); #endif } diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.cpp b/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.cpp index a7ff868..c787e1f 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.cpp +++ b/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.cpp @@ -78,13 +78,15 @@ bool SubregionStream::isExhausted() return source->isExhausted(); } + +//============================================================================== //============================================================================== #if JUCE_UNIT_TESTS struct SubregionInputStreamTests : public UnitTest { SubregionInputStreamTests() - : UnitTest ("SubregionInputStream", "Streams") + : UnitTest ("SubregionInputStream", UnitTestCategories::streams) {} void runTest() override diff --git a/JuceLibraryCode/modules/juce_core/system/juce_CompilerSupport.h b/JuceLibraryCode/modules/juce_core/system/juce_CompilerSupport.h index b1c9752..42712c7 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_CompilerSupport.h +++ b/JuceLibraryCode/modules/juce_core/system/juce_CompilerSupport.h @@ -38,8 +38,6 @@ #error "JUCE requires that GCC has C++11 compatibility enabled" #endif - #define JUCE_COMPILER_SUPPORTS_NOEXCEPT 1 - #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 500 #define JUCE_HAS_CONSTEXPR 1 #endif @@ -50,7 +48,7 @@ #endif #endif - #define JUCE_CXX14_IS_AVAILABLE (__cplusplus >= 201402L) + #define JUCE_CXX14_IS_AVAILABLE ((__cplusplus >= 201402L) || ((__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && (__cplusplus >= 201300L))) #define JUCE_CXX17_IS_AVAILABLE (__cplusplus >= 201703L) #endif @@ -63,7 +61,6 @@ #error "JUCE requires Clang 3.3 or later" #endif - #define JUCE_COMPILER_SUPPORTS_NOEXCEPT 1 #define JUCE_HAS_CONSTEXPR 1 #ifndef JUCE_COMPILER_SUPPORTS_ARC @@ -85,18 +82,11 @@ // MSVC #if JUCE_MSVC - #if _MSC_VER < 1800 // VS2013 - #error "JUCE requires Visual Studio 2013 or later" + #if _MSC_VER < 1900 // VS2015 + #error "JUCE requires Visual Studio 2015 or later" #endif - #if _MSC_VER >= 1900 // VS2015 - #define JUCE_COMPILER_SUPPORTS_NOEXCEPT 1 - #define JUCE_HAS_CONSTEXPR 1 - #else - #define _ALLOW_KEYWORD_MACROS 1 // prevent a warning - #undef noexcept - #define noexcept throw() - #endif + #define JUCE_HAS_CONSTEXPR 1 #ifndef JUCE_EXCEPTIONS_DISABLED #if ! _CPPUNWIND @@ -127,7 +117,7 @@ namespace std template unique_ptr make_unique (Args&&... args) { - return unique_ptr (new T (forward (args)...)); + return unique_ptr (new T (std::forward (args)...)); } } #endif @@ -137,5 +127,6 @@ namespace std #define JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL 1 #define JUCE_COMPILER_SUPPORTS_VARIADIC_TEMPLATES 1 #define JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS 1 + #define JUCE_COMPILER_SUPPORTS_NOEXCEPT 1 #define JUCE_DELETED_FUNCTION = delete #endif diff --git a/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h b/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h index b588a2a..8149085 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h +++ b/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h @@ -29,7 +29,7 @@ */ #define JUCE_MAJOR_VERSION 5 #define JUCE_MINOR_VERSION 4 -#define JUCE_BUILDNUMBER 3 +#define JUCE_BUILDNUMBER 7 /** Current JUCE version number. @@ -54,6 +54,11 @@ #include #include #include +#include +#include +#include +#include +#include //============================================================================== #include "juce_CompilerSupport.h" @@ -70,6 +75,9 @@ #if JUCE_MAC || JUCE_IOS #include #include + #if JUCE_IOS + #include + #endif #endif #if JUCE_LINUX @@ -112,11 +120,6 @@ #undef minor #undef KeyPress -// Include a replacement for std::function -#if JUCE_PROJUCER_LIVE_BUILD - #include "../misc/juce_StdFunctionCompat.h" -#endif - //============================================================================== // DLL building settings on Windows #if JUCE_MSVC diff --git a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp index 7d13474..2ea90f8 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp +++ b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp @@ -90,9 +90,9 @@ struct CPUInformation int numLogicalCPUs = 0, numPhysicalCPUs = 0; - bool hasMMX = false, hasSSE = false, hasSSE2 = false, hasSSE3 = false, - has3DNow = false, hasSSSE3 = false, hasSSE41 = false, - hasSSE42 = false, hasAVX = false, hasAVX2 = false, + bool hasMMX = false, hasSSE = false, hasSSE2 = false, hasSSE3 = false, + has3DNow = false, hasFMA3 = false, hasFMA4 = false, hasSSSE3 = false, + hasSSE41 = false, hasSSE42 = false, hasAVX = false, hasAVX2 = false, hasAVX512F = false, hasAVX512BW = false, hasAVX512CD = false, hasAVX512DQ = false, hasAVX512ER = false, hasAVX512IFMA = false, hasAVX512PF = false, hasAVX512VBMI = false, hasAVX512VL = false, @@ -110,6 +110,8 @@ int SystemStats::getNumCpus() noexcept { return getCPUInformation().num int SystemStats::getNumPhysicalCpus() noexcept { return getCPUInformation().numPhysicalCPUs; } bool SystemStats::hasMMX() noexcept { return getCPUInformation().hasMMX; } bool SystemStats::has3DNow() noexcept { return getCPUInformation().has3DNow; } +bool SystemStats::hasFMA3() noexcept { return getCPUInformation().hasFMA3; } +bool SystemStats::hasFMA4() noexcept { return getCPUInformation().hasFMA4; } bool SystemStats::hasSSE() noexcept { return getCPUInformation().hasSSE; } bool SystemStats::hasSSE2() noexcept { return getCPUInformation().hasSSE2; } bool SystemStats::hasSSE3() noexcept { return getCPUInformation().hasSSE3; } @@ -197,7 +199,7 @@ static LONG WINAPI handleCrash (LPEXCEPTION_POINTERS ep) static void handleCrash (int signum) { globalCrashHandler ((void*) (pointer_sized_int) signum); - kill (getpid(), SIGKILL); + ::kill (getpid(), SIGKILL); } int juce_siginterrupt (int sig, int flag); diff --git a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.h b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.h index 8e441a9..7926c32 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.h +++ b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.h @@ -61,6 +61,8 @@ public: MacOSX_10_10 = MacOSX | 10, MacOSX_10_11 = MacOSX | 11, MacOSX_10_12 = MacOSX | 12, + MacOSX_10_13 = MacOSX | 13, + MacOSX_10_14 = MacOSX | 14, Win2000 = Windows | 1, WinXP = Windows | 2, @@ -170,6 +172,8 @@ public: static bool hasMMX() noexcept; /**< Returns true if Intel MMX instructions are available. */ static bool has3DNow() noexcept; /**< Returns true if AMD 3DNOW instructions are available. */ + static bool hasFMA3() noexcept; /**< Returns true if AMD FMA3 instructions are available. */ + static bool hasFMA4() noexcept; /**< Returns true if AMD FMA4 instructions are available. */ static bool hasSSE() noexcept; /**< Returns true if Intel SSE instructions are available. */ static bool hasSSE2() noexcept; /**< Returns true if Intel SSE2 instructions are available. */ static bool hasSSE3() noexcept; /**< Returns true if Intel SSE3 instructions are available. */ diff --git a/JuceLibraryCode/modules/juce_core/system/juce_TargetPlatform.h b/JuceLibraryCode/modules/juce_core/system/juce_TargetPlatform.h index 1d4fb0c..4b9228d 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_TargetPlatform.h +++ b/JuceLibraryCode/modules/juce_core/system/juce_TargetPlatform.h @@ -68,6 +68,7 @@ #elif defined (LINUX) || defined (__linux__) #define JUCE_LINUX 1 #elif defined (__APPLE_CPP__) || defined (__APPLE_CC__) + #define CF_EXCLUDE_CSTD_HEADERS 1 #include // (needed to find out what platform we're using) #include "../native/juce_mac_ClangBugWorkaround.h" @@ -141,12 +142,12 @@ #define JUCE_INTEL 1 #endif - #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_6) - #error "To build with 10.5 compatibility, use a later SDK and set the deployment target to 10.5" + #if JUCE_MAC + #if ! defined (MAC_OS_X_VERSION_10_11) + #error "The 10.11 SDK (Xcode 7.3.1+) is required to build JUCE apps. You can create apps that run on macOS 10.7+ by changing the deployment target." + #elif MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 + #error "Building for OSX 10.6 is no longer supported!" + #endif #endif #endif @@ -182,7 +183,7 @@ //============================================================================== // Compiler type macros. -#ifdef __clang__ +#if defined (__clang__) #define JUCE_CLANG 1 #elif defined (__GNUC__) diff --git a/JuceLibraryCode/modules/juce_core/text/juce_Base64.cpp b/JuceLibraryCode/modules/juce_core/text/juce_Base64.cpp index 3b21c9c..902308b 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_Base64.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_Base64.cpp @@ -127,7 +127,9 @@ String Base64::toBase64 (const String& text) class Base64Tests : public UnitTest { public: - Base64Tests() : UnitTest ("Base64 class", "Text") {} + Base64Tests() + : UnitTest ("Base64 class", UnitTestCategories::text) + {} static MemoryBlock createRandomData (Random& r) { diff --git a/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.cpp b/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.cpp index df5b36c..d1024f0 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.cpp @@ -179,6 +179,8 @@ juce_wchar CharacterFunctions::getUnicodeCharFromWindows1252Codepage (const uint return (juce_wchar) lookup[c - 0x80]; } + +//============================================================================== //============================================================================== #if JUCE_UNIT_TESTS @@ -197,7 +199,9 @@ juce_wchar CharacterFunctions::getUnicodeCharFromWindows1252Codepage (const uint class CharacterFunctionsTests : public UnitTest { public: - CharacterFunctionsTests() : UnitTest ("CharacterFunctions", "Text") {} + CharacterFunctionsTests() + : UnitTest ("CharacterFunctions", UnitTestCategories::text) + {} void runTest() override { diff --git a/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.h b/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.h index baadfd5..53bc814 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.h @@ -151,7 +151,7 @@ public: #else JUCE_CONSTEXPR const int maxSignificantDigits = 17 + 1; // An additional digit for rounding JUCE_CONSTEXPR const int bufferSize = maxSignificantDigits + 7 + 1; // -.E-XXX and a trailing null-terminator - char buffer[bufferSize] = {}; + char buffer[(size_t) bufferSize] = {}; char* currentCharacter = &(buffer[0]); #endif @@ -505,12 +505,12 @@ public: { auto startAddress = dest.getAddress(); auto maxBytes = (ssize_t) maxBytesToWrite; - maxBytes -= sizeof (typename DestCharPointerType::CharType); // (allow for a terminating null) + maxBytes -= (ssize_t) sizeof (typename DestCharPointerType::CharType); // (allow for a terminating null) for (;;) { auto c = src.getAndAdvance(); - auto bytesNeeded = DestCharPointerType::getBytesRequiredFor (c); + auto bytesNeeded = (ssize_t) DestCharPointerType::getBytesRequiredFor (c); maxBytes -= bytesNeeded; if (c == 0 || maxBytes < 0) diff --git a/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.h b/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.h index 1ff0e47..195341e 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.h @@ -161,7 +161,7 @@ public: countries: fr be mc ch lu @endcode - The country codes are supposed to be 2-character ISO complient codes. + The country codes are supposed to be 2-character ISO compliant codes. */ const StringArray& getCountryCodes() const { return countryCodes; } diff --git a/JuceLibraryCode/modules/juce_core/text/juce_String.cpp b/JuceLibraryCode/modules/juce_core/text/juce_String.cpp index 416c39f..1681c2c 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_String.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_String.cpp @@ -56,7 +56,7 @@ struct EmptyString String::CharPointerType::CharType text; }; -static const EmptyString emptyString = { 0x3fffffff, sizeof (String::CharPointerType::CharType), 0 }; +static const EmptyString emptyString { 0x3fffffff, sizeof (String::CharPointerType::CharType), 0 }; //============================================================================== class StringHolder @@ -156,13 +156,13 @@ public: { auto* b = bufferFromText (text); - if (b != (StringHolder*) &emptyString) + if (! isEmptyString (b)) ++(b->refCount); } static inline void release (StringHolder* const b) noexcept { - if (b != (StringHolder*) &emptyString) + if (! isEmptyString (b)) if (--(b->refCount) == -1) delete[] reinterpret_cast (b); } @@ -182,7 +182,7 @@ public: { auto* b = bufferFromText (text); - if (b == (StringHolder*) &emptyString) + if (isEmptyString (b)) { auto newText = createUninitialisedBytes (numBytes); newText.writeNull(); @@ -217,6 +217,11 @@ private: - (reinterpret_cast (reinterpret_cast (128)->text) - 128)); } + static inline bool isEmptyString (StringHolder* other) + { + return (other->refCount.get() & 0x30000000) != 0; + } + void compileTimeChecks() { // Let me know if any of these assertions fail on your system! @@ -566,16 +571,16 @@ JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const wchar_t* s2) noe JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const wchar_t* s2) noexcept { return s1.compare (s2) != 0; } JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, StringRef s2) noexcept { return s1.getCharPointer().compare (s2.text) == 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, StringRef s2) noexcept { return s1.getCharPointer().compare (s2.text) != 0; } +JUCE_API bool JUCE_CALLTYPE operator< (const String& s1, StringRef s2) noexcept { return s1.getCharPointer().compare (s2.text) < 0; } +JUCE_API bool JUCE_CALLTYPE operator<= (const String& s1, StringRef s2) noexcept { return s1.getCharPointer().compare (s2.text) <= 0; } +JUCE_API bool JUCE_CALLTYPE operator> (const String& s1, StringRef s2) noexcept { return s1.getCharPointer().compare (s2.text) > 0; } +JUCE_API bool JUCE_CALLTYPE operator>= (const String& s1, StringRef s2) noexcept { return s1.getCharPointer().compare (s2.text) >= 0; } JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF8 s2) noexcept { return s1.getCharPointer().compare (s2) == 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const CharPointer_UTF8 s2) noexcept { return s1.getCharPointer().compare (s2) != 0; } JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF16 s2) noexcept { return s1.getCharPointer().compare (s2) == 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const CharPointer_UTF16 s2) noexcept { return s1.getCharPointer().compare (s2) != 0; } JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF32 s2) noexcept { return s1.getCharPointer().compare (s2) == 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const CharPointer_UTF32 s2) noexcept { return s1.getCharPointer().compare (s2) != 0; } -JUCE_API bool JUCE_CALLTYPE operator> (const String& s1, const String& s2) noexcept { return s1.compare (s2) > 0; } -JUCE_API bool JUCE_CALLTYPE operator< (const String& s1, const String& s2) noexcept { return s1.compare (s2) < 0; } -JUCE_API bool JUCE_CALLTYPE operator>= (const String& s1, const String& s2) noexcept { return s1.compare (s2) >= 0; } -JUCE_API bool JUCE_CALLTYPE operator<= (const String& s1, const String& s2) noexcept { return s1.compare (s2) <= 0; } bool String::equalsIgnoreCase (const wchar_t* const t) const noexcept { @@ -733,7 +738,7 @@ void String::appendCharPointer (const CharPointerType startOfTextToAppend, if (extraBytesNeeded > 0) { auto byteOffsetOfNull = getByteOffsetOfEnd(); - preallocateBytes (byteOffsetOfNull + (size_t) extraBytesNeeded); + preallocateBytes ((size_t) extraBytesNeeded + byteOffsetOfNull); auto* newStringStart = addBytesToPointer (text.getAddress(), (int) byteOffsetOfNull); memcpy (newStringStart, startOfTextToAppend.getAddress(), (size_t) extraBytesNeeded); @@ -1929,7 +1934,7 @@ String String::toHexString (const void* const d, const int size, const int group if (groupSize > 0) numChars += size / groupSize; - String s (PreallocationBytes (sizeof (CharPointerType::CharType) * (size_t) numChars)); + String s (PreallocationBytes ((size_t) numChars * sizeof (CharPointerType::CharType))); auto* data = static_cast (d); auto dest = s.text; @@ -2300,6 +2305,7 @@ static String serialiseDouble (double input) return reduceLengthOfFloatString (String (input, numberOfDecimalPlaces)); } + //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS @@ -2310,7 +2316,9 @@ static String serialiseDouble (double input) class StringTests : public UnitTest { public: - StringTests() : UnitTest ("String class", "Text") {} + StringTests() + : UnitTest ("String class", UnitTestCategories::text) + {} template struct TestUTFConversion diff --git a/JuceLibraryCode/modules/juce_core/text/juce_String.h b/JuceLibraryCode/modules/juce_core/text/juce_String.h index b1acf00..3a5a52d 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_String.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_String.h @@ -983,9 +983,11 @@ public: */ String (double doubleValue, int numberOfDecimalPlaces, bool useScientificNotation = false); + #ifndef DOXYGEN // Automatically creating a String from a bool opens up lots of nasty type conversion edge cases. // If you want a String representation of a bool you can cast the bool to an int first. explicit String (bool) = delete; + #endif /** Reads the value of the string as a decimal number (up to 32 bits in size). @@ -1063,7 +1065,7 @@ public: /** Returns a string containing a decimal with a set number of significant figures. - @param number the intput number + @param number the input number @param numberOfSignificantFigures the number of significant figures to use */ template @@ -1482,9 +1484,11 @@ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, float number); /** Appends a decimal number to the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, double number); +#ifndef DOXYGEN // Automatically creating a String from a bool opens up lots of nasty type conversion edge cases. // If you want a String representation of a bool you can cast the bool to an int first. String& JUCE_CALLTYPE operator<< (String&, bool) = delete; +#endif //============================================================================== /** Case-sensitive comparison of two strings. */ @@ -1513,15 +1517,6 @@ JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, CharPointer_UTF16 /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, CharPointer_UTF32 string2) noexcept; -/** Case-sensitive comparison of two strings. */ -JUCE_API bool JUCE_CALLTYPE operator> (const String& string1, const String& string2) noexcept; -/** Case-sensitive comparison of two strings. */ -JUCE_API bool JUCE_CALLTYPE operator< (const String& string1, const String& string2) noexcept; -/** Case-sensitive comparison of two strings. */ -JUCE_API bool JUCE_CALLTYPE operator>= (const String& string1, const String& string2) noexcept; -/** Case-sensitive comparison of two strings. */ -JUCE_API bool JUCE_CALLTYPE operator<= (const String& string1, const String& string2) noexcept; - //============================================================================== /** This operator allows you to write a juce String directly to std output streams. This is handy for writing strings to std::cout, std::cerr, etc. diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp b/JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp index bd10746..014d6b0 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp @@ -300,7 +300,7 @@ String StringArray::joinIntoString (StringRef separator, int start, int numberTo return strings.getReference (start); auto separatorBytes = separator.text.sizeInBytes() - sizeof (String::CharPointerType::CharType); - auto bytesNeeded = separatorBytes * (size_t) (last - start - 1); + auto bytesNeeded = (size_t) (last - start - 1) * separatorBytes; for (int i = start; i < last; ++i) bytesNeeded += strings.getReference(i).getCharPointer().sizeInBytes() - sizeof (String::CharPointerType::CharType); diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringArray.h b/JuceLibraryCode/modules/juce_core/text/juce_StringArray.h index f00e3a7..f629254 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_StringArray.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringArray.h @@ -155,12 +155,23 @@ public: /** Returns a pointer to the first String in the array. This method is provided for compatibility with standard C++ iteration mechanisms. */ - inline String* begin() const noexcept { return strings.begin(); } + inline String* begin() noexcept { return strings.begin(); } + + /** Returns a pointer to the first String in the array. + This method is provided for compatibility with standard C++ iteration mechanisms. + */ + inline const String* begin() const noexcept { return strings.begin(); } /** Returns a pointer to the String which follows the last element in the array. This method is provided for compatibility with standard C++ iteration mechanisms. */ - inline String* end() const noexcept { return strings.end(); } + inline String* end() noexcept { return strings.end(); } + + /** Returns a pointer to the String which follows the last element in the array. + This method is provided for compatibility with standard C++ iteration mechanisms. + */ + inline const String* end() const noexcept { return strings.end(); } + /** Searches for a string in the array. diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp b/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp index 0400f79..e523cdc 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp @@ -99,7 +99,7 @@ String StringPairArray::getValue (StringRef key, const String& defaultReturnValu bool StringPairArray::containsKey (StringRef key) const noexcept { - return keys.contains (key); + return keys.contains (key, ignoreCase); } void StringPairArray::set (const String& key, const String& value) diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringRef.h b/JuceLibraryCode/modules/juce_core/text/juce_StringRef.h index 82bf498..db2c726 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_StringRef.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringRef.h @@ -111,6 +111,14 @@ public: bool operator== (const String& s) const noexcept { return text.compare (s.getCharPointer()) == 0; } /** Compares this StringRef with a String. */ bool operator!= (const String& s) const noexcept { return text.compare (s.getCharPointer()) != 0; } + /** Compares this StringRef with a String. */ + bool operator< (const String& s) const noexcept { return text.compare (s.getCharPointer()) < 0; } + /** Compares this StringRef with a String. */ + bool operator<= (const String& s) const noexcept { return text.compare (s.getCharPointer()) <= 0; } + /** Compares this StringRef with a String. */ + bool operator> (const String& s) const noexcept { return text.compare (s.getCharPointer()) > 0; } + /** Compares this StringRef with a String. */ + bool operator>= (const String& s) const noexcept { return text.compare (s.getCharPointer()) >= 0; } /** Case-sensitive comparison of two StringRefs. */ bool operator== (StringRef s) const noexcept { return text.compare (s.text) == 0; } @@ -133,6 +141,14 @@ public: JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, StringRef string2) noexcept; /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, StringRef string2) noexcept; +/** Case-sensitive comparison of two strings. */ +JUCE_API bool JUCE_CALLTYPE operator< (const String& string1, StringRef string2) noexcept; +/** Case-sensitive comparison of two strings. */ +JUCE_API bool JUCE_CALLTYPE operator<= (const String& string1, StringRef string2) noexcept; +/** Case-sensitive comparison of two strings. */ +JUCE_API bool JUCE_CALLTYPE operator> (const String& string1, StringRef string2) noexcept; +/** Case-sensitive comparison of two strings. */ +JUCE_API bool JUCE_CALLTYPE operator>= (const String& string1, StringRef string2) noexcept; inline String operator+ (String s1, StringRef s2) { return s1 += String (s2.text); } inline String operator+ (StringRef s1, const String& s2) { return String (s1.text) + s2; } diff --git a/JuceLibraryCode/modules/juce_core/text/juce_TextDiff.cpp b/JuceLibraryCode/modules/juce_core/text/juce_TextDiff.cpp index 1b41f1f..89b0a9e 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_TextDiff.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_TextDiff.cpp @@ -216,6 +216,7 @@ String TextDiff::Change::appliedTo (const String& text) const noexcept return text.replaceSection (start, length, insertedText); } + //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS @@ -223,7 +224,9 @@ String TextDiff::Change::appliedTo (const String& text) const noexcept class DiffTests : public UnitTest { public: - DiffTests() : UnitTest ("TextDiff class", "Text") {} + DiffTests() + : UnitTest ("TextDiff class", UnitTestCategories::text) + {} static String createString (Random& r) { diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.cpp index 2c14b05..f734ef9 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.cpp +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.cpp @@ -80,13 +80,17 @@ String ChildProcess::readAllProcessOutput() return result.toString(); } + +//============================================================================== //============================================================================== #if JUCE_UNIT_TESTS class ChildProcessTests : public UnitTest { public: - ChildProcessTests() : UnitTest ("ChildProcess", "Threads") {} + ChildProcessTests() + : UnitTest ("ChildProcess", UnitTestCategories::threads) + {} void runTest() override { @@ -101,8 +105,8 @@ public: expect (p.start ("ls /")); #endif - //String output (p.readAllProcessOutput()); - //expect (output.isNotEmpty()); + auto output = p.readAllProcessOutput(); + expect (output.isNotEmpty()); #endif } }; diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_Process.h b/JuceLibraryCode/modules/juce_core/threads/juce_Process.h index 726fa19..bc868ef 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_Process.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_Process.h @@ -133,7 +133,7 @@ public: static void JUCE_CALLTYPE setCurrentModuleInstanceHandle (void* newHandle) noexcept; #endif - #if JUCE_MAC || DOXYGEN + #if (JUCE_MAC && JUCE_MODULE_AVAILABLE_juce_gui_basics) || DOXYGEN //============================================================================== /** OSX ONLY - Shows or hides the OSX dock icon for this app. */ static void setDockIconVisible (bool isVisible); diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.cpp index af607b7..86f6afe 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.cpp +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.cpp @@ -38,22 +38,20 @@ ReadWriteLock::~ReadWriteLock() noexcept void ReadWriteLock::enterRead() const noexcept { while (! tryEnterRead()) - waitEvent.wait (100); + readWaitEvent.wait (100); } bool ReadWriteLock::tryEnterRead() const noexcept { - const Thread::ThreadID threadId = Thread::getCurrentThreadId(); + auto threadId = Thread::getCurrentThreadId(); const SpinLock::ScopedLockType sl (accessLock); - for (int i = 0; i < readerThreads.size(); ++i) + for (auto& readerThread : readerThreads) { - ThreadRecursionCount& trc = readerThreads.getReference(i); - - if (trc.threadID == threadId) + if (readerThread.threadID == threadId) { - trc.count++; + readerThread.count++; return true; } } @@ -61,8 +59,7 @@ bool ReadWriteLock::tryEnterRead() const noexcept if (numWriters + numWaitingWriters == 0 || (threadId == writerThreadId && numWriters > 0)) { - ThreadRecursionCount trc = { threadId, 1 }; - readerThreads.add (trc); + readerThreads.add ({ threadId, 1 }); return true; } @@ -71,19 +68,21 @@ bool ReadWriteLock::tryEnterRead() const noexcept void ReadWriteLock::exitRead() const noexcept { - const Thread::ThreadID threadId = Thread::getCurrentThreadId(); + auto threadId = Thread::getCurrentThreadId(); const SpinLock::ScopedLockType sl (accessLock); for (int i = 0; i < readerThreads.size(); ++i) { - ThreadRecursionCount& trc = readerThreads.getReference(i); + auto& readerThread = readerThreads.getReference (i); - if (trc.threadID == threadId) + if (readerThread.threadID == threadId) { - if (--(trc.count) == 0) + if (--(readerThread.count) == 0) { readerThreads.remove (i); - waitEvent.signal(); + + readWaitEvent.signal(); + writeWaitEvent.signal(); } return; @@ -96,14 +95,14 @@ void ReadWriteLock::exitRead() const noexcept //============================================================================== void ReadWriteLock::enterWrite() const noexcept { - const Thread::ThreadID threadId = Thread::getCurrentThreadId(); + auto threadId = Thread::getCurrentThreadId(); const SpinLock::ScopedLockType sl (accessLock); while (! tryEnterWriteInternal (threadId)) { ++numWaitingWriters; accessLock.exit(); - waitEvent.wait (100); + writeWaitEvent.wait (100); accessLock.enter(); --numWaitingWriters; } @@ -119,7 +118,7 @@ bool ReadWriteLock::tryEnterWriteInternal (Thread::ThreadID threadId) const noex { if (readerThreads.size() + numWriters == 0 || threadId == writerThreadId - || (readerThreads.size() == 1 && readerThreads.getReference(0).threadID == threadId)) + || (readerThreads.size() == 1 && readerThreads.getReference (0).threadID == threadId)) { writerThreadId = threadId; ++numWriters; @@ -139,7 +138,9 @@ void ReadWriteLock::exitWrite() const noexcept if (--numWriters == 0) { writerThreadId = {}; - waitEvent.signal(); + + readWaitEvent.signal(); + writeWaitEvent.signal(); } } diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.h b/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.h index c954a87..28c5b72 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.h @@ -126,7 +126,7 @@ public: private: //============================================================================== SpinLock accessLock; - WaitableEvent waitEvent; + WaitableEvent readWaitEvent, writeWaitEvent; mutable int numWaitingWriters = 0, numWriters = 0; mutable Thread::ThreadID writerThreadId = {}; diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_Thread.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_Thread.cpp index b37208d..f3f8bb9 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_Thread.cpp +++ b/JuceLibraryCode/modules/juce_core/threads/juce_Thread.cpp @@ -315,7 +315,7 @@ struct LambdaThread : public Thread void run() override { fn(); - fn = {}; // free any objects that the lambda might contain while the thread is still active + fn = nullptr; // free any objects that the lambda might contain while the thread is still active } std::function fn; @@ -350,13 +350,17 @@ bool JUCE_CALLTYPE Process::isRunningUnderDebugger() noexcept return juce_isRunningUnderDebugger(); } -#if JUCE_UNIT_TESTS //============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + class AtomicTests : public UnitTest { public: - AtomicTests() : UnitTest ("Atomics", "Threads") {} + AtomicTests() + : UnitTest ("Atomics", UnitTestCategories::threads) + {} void runTest() override { @@ -480,7 +484,7 @@ class ThreadLocalValueUnitTest : public UnitTest, { public: ThreadLocalValueUnitTest() - : UnitTest ("ThreadLocalValue", "Threads"), + : UnitTest ("ThreadLocalValue", UnitTestCategories::threads), Thread ("ThreadLocalValue Thread") {} diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.cpp index ea8ac60..db9cb8d 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.cpp +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.cpp @@ -176,6 +176,7 @@ void ThreadPool::addJob (std::function jobToRun) int ThreadPool::getNumJobs() const noexcept { + const ScopedLock sl (lock); return jobs.size(); } diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.h b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.h index d39ca67..21973e1 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.h @@ -76,7 +76,7 @@ public: again when a thread is free. */ }; - /** Peforms the actual work that this job needs to do. + /** Performs the actual work that this job needs to do. Your subclass must implement this method, in which is does its work. diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.h b/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.h index 61b0ea2..f661cff 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.h @@ -57,7 +57,7 @@ public: @returns Your method should return the number of milliseconds which it would like to wait before being called again. Returning 0 will make the thread call again as soon as possible (after possibly servicing other busy clients). If you return a value below zero, your client will be removed from the list of clients, - and won't be called again. The value you specify isn't a guaranteee, and is only used as a hint by the + and won't be called again. The value you specify isn't a guarantee, and is only used as a hint by the thread - the actual time before the next callback may be more or less than specified. You can force the TimeSliceThread to wake up and poll again immediately by calling its notify() method. */ diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.cpp new file mode 100644 index 0000000..8b01c4d --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.cpp @@ -0,0 +1,70 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2017 - ROLI Ltd. + + JUCE is an open source library subject to commercial or open-source + licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. 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. + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +WaitableEvent::WaitableEvent (bool manualReset) noexcept + : useManualReset (manualReset) +{ +} + +bool WaitableEvent::wait (int timeOutMilliseconds) const +{ + std::unique_lock lock (mutex); + + if (! triggered) + { + if (timeOutMilliseconds < 0) + { + condition.wait (lock, [this] { return triggered == true; }); + } + else + { + if (! condition.wait_for (lock, std::chrono::milliseconds (timeOutMilliseconds), + [this] { return triggered == true; })) + { + return false; + } + } + } + + if (! useManualReset) + reset(); + + return true; +} + +void WaitableEvent::signal() const +{ + std::unique_lock lock (mutex); + + triggered = true; + condition.notify_all(); +} + +void WaitableEvent::reset() const +{ + triggered = false; +} + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.h b/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.h index 012b83a..3e31a97 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.h @@ -46,13 +46,6 @@ public: */ explicit WaitableEvent (bool manualReset = false) noexcept; - /** Destructor. - - If other threads are waiting on this object when it gets deleted, this - can cause nasty errors, so be careful! - */ - ~WaitableEvent() noexcept; - //============================================================================== /** Suspends the calling thread until the event has been signalled. @@ -68,9 +61,8 @@ public: @returns true if the object has been signalled, false if the timeout expires first. @see signal, reset */ - bool wait (int timeOutMilliseconds = -1) const noexcept; + bool wait (int timeOutMilliseconds = -1) const; - //============================================================================== /** Wakes up any threads that are currently waiting on this object. If signal() is called when nothing is waiting, the next thread to call wait() @@ -86,24 +78,20 @@ public: @see wait, reset */ - void signal() const noexcept; + void signal() const; - //============================================================================== /** Resets the event to an unsignalled state. If it's not already signalled, this does nothing. */ - void reset() const noexcept; - + void reset() const; private: //============================================================================== - #if JUCE_WINDOWS - void* handle; - #else - mutable pthread_cond_t condition; - mutable pthread_mutex_t mutex; - mutable bool triggered, manualReset; - #endif + bool useManualReset; + + mutable std::mutex mutex; + mutable std::condition_variable condition; + mutable std::atomic triggered { false }; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WaitableEvent) }; diff --git a/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.cpp b/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.cpp index 4650e16..5e15212 100644 --- a/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.cpp +++ b/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.cpp @@ -43,7 +43,8 @@ PerformanceCounter::PerformanceCounter (const String& name, int runsPerPrintout, PerformanceCounter::~PerformanceCounter() { - printStatistics(); + if (stats.numRuns > 0) + printStatistics(); } PerformanceCounter::Statistics::Statistics() noexcept diff --git a/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.cpp b/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.cpp index 3dc2fd4..0775054 100644 --- a/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.cpp +++ b/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.cpp @@ -28,13 +28,13 @@ RelativeTime::RelativeTime (const RelativeTime& other) noexcept : numSeconds ( RelativeTime::~RelativeTime() noexcept {} //============================================================================== -RelativeTime RelativeTime::milliseconds (const int milliseconds) noexcept { return RelativeTime (milliseconds * 0.001); } -RelativeTime RelativeTime::milliseconds (const int64 milliseconds) noexcept { return RelativeTime (milliseconds * 0.001); } +RelativeTime RelativeTime::milliseconds (int milliseconds) noexcept { return RelativeTime (milliseconds * 0.001); } +RelativeTime RelativeTime::milliseconds (int64 milliseconds) noexcept { return RelativeTime (milliseconds * 0.001); } RelativeTime RelativeTime::seconds (double s) noexcept { return RelativeTime (s); } -RelativeTime RelativeTime::minutes (const double numberOfMinutes) noexcept { return RelativeTime (numberOfMinutes * 60.0); } -RelativeTime RelativeTime::hours (const double numberOfHours) noexcept { return RelativeTime (numberOfHours * (60.0 * 60.0)); } -RelativeTime RelativeTime::days (const double numberOfDays) noexcept { return RelativeTime (numberOfDays * (60.0 * 60.0 * 24.0)); } -RelativeTime RelativeTime::weeks (const double numberOfWeeks) noexcept { return RelativeTime (numberOfWeeks * (60.0 * 60.0 * 24.0 * 7.0)); } +RelativeTime RelativeTime::minutes (double numberOfMinutes) noexcept { return RelativeTime (numberOfMinutes * 60.0); } +RelativeTime RelativeTime::hours (double numberOfHours) noexcept { return RelativeTime (numberOfHours * (60.0 * 60.0)); } +RelativeTime RelativeTime::days (double numberOfDays) noexcept { return RelativeTime (numberOfDays * (60.0 * 60.0 * 24.0)); } +RelativeTime RelativeTime::weeks (double numberOfWeeks) noexcept { return RelativeTime (numberOfWeeks * (60.0 * 60.0 * 24.0 * 7.0)); } //============================================================================== int64 RelativeTime::inMilliseconds() const noexcept { return (int64) (numSeconds * 1000.0); } @@ -48,8 +48,8 @@ RelativeTime& RelativeTime::operator= (const RelativeTime& other) noexcept RelativeTime RelativeTime::operator+= (RelativeTime t) noexcept { numSeconds += t.numSeconds; return *this; } RelativeTime RelativeTime::operator-= (RelativeTime t) noexcept { numSeconds -= t.numSeconds; return *this; } -RelativeTime RelativeTime::operator+= (const double secs) noexcept { numSeconds += secs; return *this; } -RelativeTime RelativeTime::operator-= (const double secs) noexcept { numSeconds -= secs; return *this; } +RelativeTime RelativeTime::operator+= (double secs) noexcept { numSeconds += secs; return *this; } +RelativeTime RelativeTime::operator-= (double secs) noexcept { numSeconds -= secs; return *this; } JUCE_API RelativeTime JUCE_CALLTYPE operator+ (RelativeTime t1, RelativeTime t2) noexcept { return t1 += t2; } JUCE_API RelativeTime JUCE_CALLTYPE operator- (RelativeTime t1, RelativeTime t2) noexcept { return t1 -= t2; } @@ -62,77 +62,96 @@ JUCE_API bool JUCE_CALLTYPE operator>= (RelativeTime t1, RelativeTime t2) noexce JUCE_API bool JUCE_CALLTYPE operator<= (RelativeTime t1, RelativeTime t2) noexcept { return t1.inSeconds() <= t2.inSeconds(); } //============================================================================== -static void translateTimeField (String& result, int n, const char* singular, const char* plural) +static String translateTimeField (int n, const char* singular, const char* plural) { - result << TRANS (n == 1 ? singular : plural) - .replace (n == 1 ? "1" : "2", String (n)) - << ' '; + return TRANS (n == 1 ? singular : plural).replace (n == 1 ? "1" : "2", String (n)); +} + +static String describeYears (int n) { return translateTimeField (n, NEEDS_TRANS("1 year"), NEEDS_TRANS("2 years")); } +static String describeMonths (int n) { return translateTimeField (n, NEEDS_TRANS("1 month"), NEEDS_TRANS("2 months")); } +static String describeWeeks (int n) { return translateTimeField (n, NEEDS_TRANS("1 week"), NEEDS_TRANS("2 weeks")); } +static String describeDays (int n) { return translateTimeField (n, NEEDS_TRANS("1 day"), NEEDS_TRANS("2 days")); } +static String describeHours (int n) { return translateTimeField (n, NEEDS_TRANS("1 hr"), NEEDS_TRANS("2 hrs")); } +static String describeMinutes (int n) { return translateTimeField (n, NEEDS_TRANS("1 min"), NEEDS_TRANS("2 mins")); } +static String describeSeconds (int n) { return translateTimeField (n, NEEDS_TRANS("1 sec"), NEEDS_TRANS("2 secs")); } + +String RelativeTime::getApproximateDescription() const +{ + if (numSeconds <= 1.0) + return "< 1 sec"; + + auto weeks = (int) inWeeks(); + + if (weeks > 52) return describeYears (weeks / 52); + if (weeks > 8) return describeMonths ((weeks * 12) / 52); + if (weeks > 1) return describeWeeks (weeks); + + auto days = (int) inWeeks(); + + if (days > 1) + return describeDays (days); + + auto hours = (int) inHours(); + + if (hours > 0) + return describeHours (hours); + + auto minutes = (int) inMinutes(); + + if (minutes > 0) + return describeMinutes (minutes); + + return describeSeconds ((int) numSeconds); } String RelativeTime::getDescription (const String& returnValueForZeroTime) const { - if (numSeconds < 0.001 && numSeconds > -0.001) + if (std::abs (numSeconds) < 0.001) return returnValueForZeroTime; - String result; - result.preallocateBytes (32); - if (numSeconds < 0) - result << '-'; + return "-" + RelativeTime (-numSeconds).getDescription(); + + StringArray fields; + + auto n = (int) inWeeks(); - int fieldsShown = 0; - int n = std::abs ((int) inWeeks()); if (n > 0) - { - translateTimeField (result, n, NEEDS_TRANS("1 week"), NEEDS_TRANS("2 weeks")); - ++fieldsShown; - } + fields.add (describeWeeks (n)); + + n = ((int) inDays()) % 7; - n = std::abs ((int) inDays()) % 7; if (n > 0) - { - translateTimeField (result, n, NEEDS_TRANS("1 day"), NEEDS_TRANS("2 days")); - ++fieldsShown; - } + fields.add (describeDays (n)); - if (fieldsShown < 2) + if (fields.size() < 2) { - n = std::abs ((int) inHours()) % 24; + n = ((int) inHours()) % 24; + if (n > 0) - { - translateTimeField (result, n, NEEDS_TRANS("1 hr"), NEEDS_TRANS("2 hrs")); - ++fieldsShown; - } + fields.add (describeHours (n)); - if (fieldsShown < 2) + if (fields.size() < 2) { - n = std::abs ((int) inMinutes()) % 60; + n = ((int) inMinutes()) % 60; + if (n > 0) - { - translateTimeField (result, n, NEEDS_TRANS("1 min"), NEEDS_TRANS("2 mins")); - ++fieldsShown; - } + fields.add (describeMinutes (n)); - if (fieldsShown < 2) + if (fields.size() < 2) { - n = std::abs ((int) inSeconds()) % 60; + n = ((int) inSeconds()) % 60; + if (n > 0) - { - translateTimeField (result, n, NEEDS_TRANS("1 sec"), NEEDS_TRANS("2 secs")); - ++fieldsShown; - } + fields.add (describeSeconds (n)); - if (fieldsShown == 0) - { - n = std::abs ((int) inMilliseconds()) % 1000; - if (n > 0) - result << n << ' ' << TRANS ("ms"); - } + if (fields.isEmpty()) + fields.add (String (((int) inMilliseconds()) % 1000) + " " + TRANS ("ms")); } } } - return result.trimEnd(); + return fields.joinIntoString (" "); } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.h b/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.h index 8bda482..e9741d2 100644 --- a/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.h +++ b/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.h @@ -137,6 +137,13 @@ public: */ String getDescription (const String& returnValueForZeroTime = "0") const; + //============================================================================== + /** This returns a string that roughly describes how long ago this time was, which + can be handy for showing ages of files, etc. + This will only attempt to be accurate to within the nearest order of magnitude + so returns strings such as "5 years", "2 weeks", "< 1 minute", "< 1 sec" etc. + */ + String getApproximateDescription() const; //============================================================================== /** Adds another RelativeTime to this one. */ diff --git a/JuceLibraryCode/modules/juce_core/time/juce_Time.cpp b/JuceLibraryCode/modules/juce_core/time/juce_Time.cpp index 17ec287..4d7d5d1 100644 --- a/JuceLibraryCode/modules/juce_core/time/juce_Time.cpp +++ b/JuceLibraryCode/modules/juce_core/time/juce_Time.cpp @@ -491,7 +491,7 @@ Time Time::fromISO8601 (StringRef iso) if (seconds < 0) return {}; - if (*t == '.') + if (*t == '.' || *t == ',') { ++t; milliseconds = parseFixedSizeIntAndSkip (t, 3, 0); @@ -612,7 +612,9 @@ Time Time::getCompilationDate() class TimeTests : public UnitTest { public: - TimeTests() : UnitTest ("Time", "Time") {} + TimeTests() + : UnitTest ("Time", UnitTestCategories::time) + {} void runTest() override { @@ -632,15 +634,22 @@ public: expect (Time::fromISO8601 (t.toISO8601 (false)) == t); expect (Time::fromISO8601 ("2016-02-16") == Time (2016, 1, 16, 0, 0, 0, 0, false)); - expect (Time::fromISO8601 ("20160216Z") == Time (2016, 1, 16, 0, 0, 0, 0, false)); + expect (Time::fromISO8601 ("20160216Z") == Time (2016, 1, 16, 0, 0, 0, 0, false)); + expect (Time::fromISO8601 ("2016-02-16T15:03:57+00:00") == Time (2016, 1, 16, 15, 3, 57, 0, false)); - expect (Time::fromISO8601 ("20160216T150357+0000") == Time (2016, 1, 16, 15, 3, 57, 0, false)); + expect (Time::fromISO8601 ("20160216T150357+0000") == Time (2016, 1, 16, 15, 3, 57, 0, false)); + expect (Time::fromISO8601 ("2016-02-16T15:03:57.999+00:00") == Time (2016, 1, 16, 15, 3, 57, 999, false)); - expect (Time::fromISO8601 ("20160216T150357.999+0000") == Time (2016, 1, 16, 15, 3, 57, 999, false)); - expect (Time::fromISO8601 ("2016-02-16T15:03:57.999Z") == Time (2016, 1, 16, 15, 3, 57, 999, false)); - expect (Time::fromISO8601 ("20160216T150357.999Z") == Time (2016, 1, 16, 15, 3, 57, 999, false)); + expect (Time::fromISO8601 ("20160216T150357.999+0000") == Time (2016, 1, 16, 15, 3, 57, 999, false)); + expect (Time::fromISO8601 ("2016-02-16T15:03:57.999Z") == Time (2016, 1, 16, 15, 3, 57, 999, false)); + expect (Time::fromISO8601 ("2016-02-16T15:03:57,999Z") == Time (2016, 1, 16, 15, 3, 57, 999, false)); + expect (Time::fromISO8601 ("20160216T150357.999Z") == Time (2016, 1, 16, 15, 3, 57, 999, false)); + expect (Time::fromISO8601 ("20160216T150357,999Z") == Time (2016, 1, 16, 15, 3, 57, 999, false)); + expect (Time::fromISO8601 ("2016-02-16T15:03:57.999-02:30") == Time (2016, 1, 16, 17, 33, 57, 999, false)); - expect (Time::fromISO8601 ("20160216T150357.999-0230") == Time (2016, 1, 16, 17, 33, 57, 999, false)); + expect (Time::fromISO8601 ("2016-02-16T15:03:57,999-02:30") == Time (2016, 1, 16, 17, 33, 57, 999, false)); + expect (Time::fromISO8601 ("20160216T150357.999-0230") == Time (2016, 1, 16, 17, 33, 57, 999, false)); + expect (Time::fromISO8601 ("20160216T150357,999-0230") == Time (2016, 1, 16, 17, 33, 57, 999, false)); expect (Time (1970, 0, 1, 0, 0, 0, 0, false) == Time (0)); expect (Time (2106, 1, 7, 6, 28, 15, 0, false) == Time (4294967295000)); diff --git a/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTestCategories.h b/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTestCategories.h new file mode 100644 index 0000000..fdc1d44 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTestCategories.h @@ -0,0 +1,53 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2017 - ROLI Ltd. + + JUCE is an open source library subject to commercial or open-source + licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. 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. + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +namespace UnitTestCategories +{ + static const String analytics { "Analytics" }; + static const String audio { "Audio" }; + static const String audioProcessorParameters { "AudioProcessorParameters" }; + static const String blocks { "Blocks" }; + static const String compression { "Compression" }; + static const String containers { "Containers" }; + static const String cryptography { "Cryptography" }; + static const String dsp { "DSP" }; + static const String files { "Files" }; + static const String function { "Function" }; + static const String gui { "GUI" }; + static const String json { "JSON" }; + static const String maths { "Maths" }; + static const String midi { "MIDI" }; + static const String networking { "Networking" }; + static const String osc { "OSC" }; + static const String smoothedValues { "SmoothedValues" }; + static const String streams { "Streams" }; + static const String text { "Text" }; + static const String threads { "Threads" }; + static const String time { "Time" }; + static const String values { "Values" }; + static const String xml { "XML" }; +} + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.cpp b/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.cpp index 3132841..040ebc0 100644 --- a/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.cpp +++ b/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.cpp @@ -28,26 +28,34 @@ XmlDocument::XmlDocument (const File& file) : inputSource (new FileInputSource XmlDocument::~XmlDocument() {} -XmlElement* XmlDocument::parse (const File& file) +std::unique_ptr XmlDocument::parse (const File& file) { - XmlDocument doc (file); - return doc.getDocumentElement(); + return XmlDocument (file).getDocumentElement(); } -XmlElement* XmlDocument::parse (const String& xmlData) +std::unique_ptr XmlDocument::parse (const String& textToParse) { - XmlDocument doc (xmlData); - return doc.getDocumentElement(); + return XmlDocument (textToParse).getDocumentElement(); } std::unique_ptr parseXML (const String& textToParse) { - return std::unique_ptr (XmlDocument::parse (textToParse)); + return XmlDocument (textToParse).getDocumentElement(); } -std::unique_ptr parseXML (const File& fileToParse) +std::unique_ptr parseXML (const File& file) { - return std::unique_ptr (XmlDocument::parse (fileToParse)); + return XmlDocument (file).getDocumentElement(); +} + +std::unique_ptr parseXMLIfTagMatches (const String& textToParse, StringRef requiredTag) +{ + return XmlDocument (textToParse).getDocumentElementIfTagMatches (requiredTag); +} + +std::unique_ptr parseXMLIfTagMatches (const File& file, StringRef requiredTag) +{ + return XmlDocument (file).getDocumentElementIfTagMatches (requiredTag); } void XmlDocument::setInputSource (InputSource* newSource) noexcept @@ -72,7 +80,7 @@ namespace XmlIdentifierChars { static const uint32 legalChars[] = { 0, 0x7ff6000, 0x87fffffe, 0x7fffffe, 0 }; - return ((int) c < (int) numElementsInArray (legalChars) * 32) ? ((legalChars [c >> 5] & (1 << (c & 31))) != 0) + return ((int) c < (int) numElementsInArray (legalChars) * 32) ? ((legalChars [c >> 5] & (uint32) (1 << (c & 31))) != 0) : isIdentifierCharSlow (c); } @@ -99,7 +107,7 @@ namespace XmlIdentifierChars } } -XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentElement) +std::unique_ptr XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentElement) { if (originalText.isEmpty() && inputSource != nullptr) { @@ -139,6 +147,15 @@ XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentEle return parseDocumentElement (originalText.getCharPointer(), onlyReadOuterDocumentElement); } +std::unique_ptr XmlDocument::getDocumentElementIfTagMatches (StringRef requiredTag) +{ + if (auto xml = getDocumentElement (true)) + if (xml->hasTagName (requiredTag)) + return getDocumentElement (false); + + return {}; +} + const String& XmlDocument::getLastParseError() const noexcept { return lastError; @@ -176,8 +193,8 @@ juce_wchar XmlDocument::readNextChar() noexcept return c; } -XmlElement* XmlDocument::parseDocumentElement (String::CharPointerType textToParse, - const bool onlyReadOuterDocumentElement) +std::unique_ptr XmlDocument::parseDocumentElement (String::CharPointerType textToParse, + bool onlyReadOuterDocumentElement) { input = textToParse; errorOccurred = false; @@ -202,10 +219,10 @@ XmlElement* XmlDocument::parseDocumentElement (String::CharPointerType textToPar std::unique_ptr result (readNextElement (! onlyReadOuterDocumentElement)); if (! errorOccurred) - return result.release(); + return result; } - return nullptr; + return {}; } bool XmlDocument::parseHeader() diff --git a/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.h b/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.h index 267d565..0b200f0 100644 --- a/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.h +++ b/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.h @@ -32,19 +32,16 @@ namespace juce e.g. @code - XmlDocument myDocument (File ("myfile.xml")); - std::unique_ptr mainElement (myDocument.getDocumentElement()); - if (mainElement == nullptr) - { - String error = myDocument.getLastParseError(); - } - else + if (auto mainElement = myDocument.getDocumentElement()) { ..use the element } - + else + { + String error = myDocument.getLastParseError(); + } @endcode Or you can use the helper functions for much less verbose parsing.. @@ -98,12 +95,17 @@ public: tag, without having to parse the entire file @returns a new XmlElement which the caller will need to delete, or null if there was an error. - @see getLastParseError + @see getLastParseError, getDocumentElementIfTagMatches */ - XmlElement* getDocumentElement (bool onlyReadOuterDocumentElement = false); + std::unique_ptr getDocumentElement (bool onlyReadOuterDocumentElement = false); + + /** Does an inexpensive check to see whether the outer element has the given tag name, and + then does a full parse if it matches. + If the tag is different, or the XML parse fails, this will return nullptr. + */ + std::unique_ptr getDocumentElementIfTagMatches (StringRef requiredTag); /** Returns the parsing error that occurred the last time getDocumentElement was called. - @returns the error, or an empty string if there was no error. */ const String& getLastParseError() const noexcept; @@ -136,14 +138,14 @@ public: An even better shortcut is the juce::parseXML() function, which returns a std::unique_ptr! @returns a new XmlElement which the caller will need to delete, or null if there was an error. */ - static XmlElement* parse (const File& file); + static std::unique_ptr parse (const File& file); /** A handy static method that parses some XML data. This is a shortcut for creating an XmlDocument object and calling getDocumentElement() on it. An even better shortcut is the juce::parseXML() function, which returns a std::unique_ptr! @returns a new XmlElement which the caller will need to delete, or null if there was an error. */ - static XmlElement* parse (const String& xmlData); + static std::unique_ptr parse (const String& xmlData); //============================================================================== @@ -156,7 +158,7 @@ private: bool needToLoadDTD = false, ignoreEmptyTextElements = true; std::unique_ptr inputSource; - XmlElement* parseDocumentElement (String::CharPointerType, bool outer); + std::unique_ptr parseDocumentElement (String::CharPointerType, bool outer); void setLastError (const String&, bool carryOn); bool parseHeader(); bool parseDTD(); @@ -178,18 +180,31 @@ private: //============================================================================== /** Attempts to parse some XML text, returning a new XmlElement if it was valid. If the parse fails, this will return a nullptr - if you need more information about - errors or more parsing options, see the XmlDocument instead. - @see XmlDocument + errors or more parsing options, see the XmlDocument class instead. + @see XmlDocument, parseXMLIfTagMatches */ std::unique_ptr parseXML (const String& textToParse); /** Attempts to parse some XML text, returning a new XmlElement if it was valid. If the parse fails, this will return a nullptr - if you need more information about - errors or more parsing options, see the XmlDocument instead. - @see XmlDocument + errors or more parsing options, see the XmlDocument class instead. + @see XmlDocument, parseXMLIfTagMatches */ std::unique_ptr parseXML (const File& fileToParse); +/** Does an inexpensive check to see whether the top-level element has the given tag + name, and if that's true, does a full parse and returns the result. + If the outer tag doesn't match, or the XML has errors, this will return nullptr; + @see parseXML +*/ +std::unique_ptr parseXMLIfTagMatches (const String& textToParse, StringRef requiredTag); + +/** Does an inexpensive check to see whether the top-level element has the given tag + name, and if that's true, does a full parse and returns the result. + If the outer tag doesn't match, or the XML has errors, this will return nullptr; + @see parseXML +*/ +std::unique_ptr parseXMLIfTagMatches (const File& fileToParse, StringRef requiredTag); } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.cpp b/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.cpp index 91f6479..1e3bdd4 100644 --- a/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.cpp +++ b/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.cpp @@ -167,50 +167,46 @@ XmlElement::~XmlElement() noexcept //============================================================================== namespace XmlOutputFunctions { - #if 0 // (These functions are just used to generate the lookup table used below) - bool isLegalXmlCharSlow (const juce_wchar character) noexcept + namespace LegalCharLookupTable { - if ((character >= 'a' && character <= 'z') - || (character >= 'A' && character <= 'Z') - || (character >= '0' && character <= '9')) - return true; - - const char* t = " .,;:-()_+=?!'#@[]/\\*%~{}$|"; - - do + template + struct Bit { - if (((juce_wchar) (uint8) *t) == character) - return true; + enum { v = ((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') + || c == ' ' || c == '.' || c == ',' || c == ';' + || c == ':' || c == '-' || c == '(' || c == ')' + || c == '_' || c == '+' || c == '=' || c == '?' + || c == '!' || c == '$' || c == '#' || c == '@' + || c == '[' || c == ']' || c == '/' || c == '|' + || c == '*' || c == '%' || c == '~' || c == '{' + || c == '}' || c == '\'' || c == '\\') + ? (1 << (c & 7)) : 0 }; + }; + + template + struct Byte + { + enum { v = (int) Bit::v | (int) Bit::v + | (int) Bit::v | (int) Bit::v + | (int) Bit::v | (int) Bit::v + | (int) Bit::v | (int) Bit::v }; + }; + + static bool isLegal (uint32 c) noexcept + { + static const unsigned char legalChars[] = { Byte< 0>::v, Byte< 1>::v, Byte< 2>::v, Byte< 3>::v, + Byte< 4>::v, Byte< 5>::v, Byte< 6>::v, Byte< 7>::v, + Byte< 8>::v, Byte< 9>::v, Byte<10>::v, Byte<11>::v, + Byte<12>::v, Byte<13>::v, Byte<14>::v, Byte<15>::v }; + + return c < sizeof (legalChars) * 8 + && (legalChars[c >> 3] & (1 << (c & 7))) != 0; } - while (*++t != 0); - - return false; } - void generateLegalCharLookupTable() - { - uint8 n[32] = { 0 }; - for (int i = 0; i < 256; ++i) - if (isLegalXmlCharSlow (i)) - n[i >> 3] |= (1 << (i & 7)); - - String s; - for (int i = 0; i < 32; ++i) - s << (int) n[i] << ", "; - - DBG (s); - } - #endif - - static bool isLegalXmlChar (const uint32 c) noexcept - { - static const unsigned char legalChars[] = { 0, 0, 0, 0, 187, 255, 255, 175, 255, - 255, 255, 191, 254, 255, 255, 127 }; - return c < sizeof (legalChars) * 8 - && (legalChars [c >> 3] & (1 << (c & 7))) != 0; - } - - static void escapeIllegalXmlChars (OutputStream& outputStream, const String& text, const bool changeNewLines) + static void escapeIllegalXmlChars (OutputStream& outputStream, const String& text, bool changeNewLines) { auto t = text.getCharPointer(); @@ -221,7 +217,7 @@ namespace XmlOutputFunctions if (character == 0) break; - if (isLegalXmlChar (character)) + if (LegalCharLookupTable::isLegal (character)) { outputStream << (char) character; } @@ -257,13 +253,12 @@ namespace XmlOutputFunctions } void XmlElement::writeElementAsText (OutputStream& outputStream, - const int indentationLevel, - const int lineWrapLength) const + int indentationLevel, + int lineWrapLength, + const char* newLineChars) const { - using namespace XmlOutputFunctions; - if (indentationLevel >= 0) - writeSpaces (outputStream, (size_t) indentationLevel); + XmlOutputFunctions::writeSpaces (outputStream, (size_t) indentationLevel); if (! isTextElement()) { @@ -278,8 +273,8 @@ void XmlElement::writeElementAsText (OutputStream& outputStream, { if (lineLen > lineWrapLength && indentationLevel >= 0) { - outputStream << newLine; - writeSpaces (outputStream, attIndent); + outputStream << newLineChars; + XmlOutputFunctions::writeSpaces (outputStream, attIndent); lineLen = 0; } @@ -287,7 +282,7 @@ void XmlElement::writeElementAsText (OutputStream& outputStream, outputStream.writeByte (' '); outputStream << att->name; outputStream.write ("=\"", 2); - escapeIllegalXmlChars (outputStream, att->value, true); + XmlOutputFunctions::escapeIllegalXmlChars (outputStream, att->value, true); outputStream.writeByte ('"'); lineLen += (int) (outputStream.getPosition() - startPos); } @@ -302,24 +297,25 @@ void XmlElement::writeElementAsText (OutputStream& outputStream, { if (child->isTextElement()) { - escapeIllegalXmlChars (outputStream, child->getText(), false); + XmlOutputFunctions::escapeIllegalXmlChars (outputStream, child->getText(), false); lastWasTextNode = true; } else { if (indentationLevel >= 0 && ! lastWasTextNode) - outputStream << newLine; + outputStream << newLineChars; child->writeElementAsText (outputStream, - lastWasTextNode ? 0 : (indentationLevel + (indentationLevel >= 0 ? 2 : 0)), lineWrapLength); + lastWasTextNode ? 0 : (indentationLevel + (indentationLevel >= 0 ? 2 : 0)), lineWrapLength, + newLineChars); lastWasTextNode = false; } } if (indentationLevel >= 0 && ! lastWasTextNode) { - outputStream << newLine; - writeSpaces (outputStream, (size_t) indentationLevel); + outputStream << newLineChars; + XmlOutputFunctions::writeSpaces (outputStream, (size_t) indentationLevel); } outputStream.write (""; + output << options.customHeader; - if (allOnOneLine) + if (options.newLineChars == nullptr) output.writeByte (' '); else - output << newLine << newLine; + output << options.newLineChars + << options.newLineChars; } - - if (dtdToUse.isNotEmpty()) + else if (options.addDefaultHeader) { - output << dtdToUse; + output << ""; + + if (options.newLineChars == nullptr) output.writeByte (' '); else - output << newLine; + output << options.newLineChars + << options.newLineChars; } - writeElementAsText (output, allOnOneLine ? -1 : 0, lineWrapLength); + if (options.dtd.isNotEmpty()) + { + output << options.dtd; - if (! allOnOneLine) - output << newLine; + if (options.newLineChars == nullptr) + output.writeByte (' '); + else + output << options.newLineChars; + } + + writeElementAsText (output, options.newLineChars == nullptr ? -1 : 0, + options.lineWrapLength, + options.newLineChars); + + if (options.newLineChars != nullptr) + output << options.newLineChars; } -bool XmlElement::writeToFile (const File& file, StringRef dtdToUse, - StringRef encodingType, int lineWrapLength) const +bool XmlElement::writeTo (const File& destinationFile, const TextFormat& options) const { - TemporaryFile tempFile (file); + TemporaryFile tempFile (destinationFile); { FileOutputStream out (tempFile.getFile()); @@ -392,7 +414,7 @@ bool XmlElement::writeToFile (const File& file, StringRef dtdToUse, if (! out.openedOk()) return false; - writeToStream (out, dtdToUse, false, true, encodingType, lineWrapLength); + writeTo (out, options); out.flush(); // (called explicitly to force an fsync on posix) if (out.getStatus().failed()) @@ -402,6 +424,48 @@ bool XmlElement::writeToFile (const File& file, StringRef dtdToUse, return tempFile.overwriteTargetFileWithTemporary(); } +String XmlElement::createDocument (StringRef dtdToUse, bool allOnOneLine, bool includeXmlHeader, + StringRef encodingType, int lineWrapLength) const +{ + TextFormat options; + options.dtd = dtdToUse; + options.customEncoding = encodingType; + options.addDefaultHeader = includeXmlHeader; + options.lineWrapLength = lineWrapLength; + + if (allOnOneLine) + options.newLineChars = nullptr; + + return toString (options); +} + +void XmlElement::writeToStream (OutputStream& output, StringRef dtdToUse, + bool allOnOneLine, bool includeXmlHeader, + StringRef encodingType, int lineWrapLength) const +{ + TextFormat options; + options.dtd = dtdToUse; + options.customEncoding = encodingType; + options.addDefaultHeader = includeXmlHeader; + options.lineWrapLength = lineWrapLength; + + if (allOnOneLine) + options.newLineChars = nullptr; + + writeTo (output, options); +} + +bool XmlElement::writeToFile (const File& file, StringRef dtdToUse, + StringRef encodingType, int lineWrapLength) const +{ + TextFormat options; + options.dtd = dtdToUse; + options.customEncoding = encodingType; + options.lineWrapLength = lineWrapLength; + + return writeTo (file, options); +} + //============================================================================== bool XmlElement::hasTagName (StringRef possibleTagName) const noexcept { @@ -608,7 +672,7 @@ int XmlElement::getNumChildElements() const noexcept XmlElement* XmlElement::getChildElement (const int index) const noexcept { - return firstChildElement [index].get(); + return firstChildElement[index].get(); } XmlElement* XmlElement::getChildByName (StringRef childName) const noexcept @@ -925,13 +989,16 @@ void XmlElement::deleteAllTextElements() noexcept } } +//============================================================================== //============================================================================== #if JUCE_UNIT_TESTS class XmlElementTests : public UnitTest { public: - XmlElementTests() : UnitTest ("XmlElement", "XML") {} + XmlElementTests() + : UnitTest ("XmlElement", UnitTestCategories::xml) + {} void runTest() override { diff --git a/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.h b/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.h index 91de127..91079cf 100644 --- a/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.h +++ b/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.h @@ -126,11 +126,11 @@ namespace juce animalsList.addChildElement (giraffe); } - // now we can turn the whole thing into a text document.. - String myXmlDoc = animalsList.createDocument (String()); + // now we can turn the whole thing into textual XML + auto xmlString = animalsList.toString(); @endcode - @see XmlDocument + @see parseXML, parseXMLIfTagMatches, XmlDocument @tags{Core} */ @@ -184,74 +184,41 @@ public: bool ignoreOrderOfAttributes) const noexcept; //============================================================================== - /** Returns an XML text document that represents this element. - - The string returned can be parsed to recreate the same XmlElement that - was used to create it. - - @param dtdToUse the DTD to add to the document - @param allOnOneLine if true, this means that the document will not contain any - linefeeds, so it'll be smaller but not very easy to read. - @param includeXmlHeader whether to add the "::Appender; friend class NamedValueSet; - LinkedListPointer nextListItem; - LinkedListPointer firstChildElement; + LinkedListPointer nextListItem, firstChildElement; LinkedListPointer attributes; String tagName; XmlElement (int) noexcept; void copyChildrenAndAttributesFrom (const XmlElement&); - void writeElementAsText (OutputStream&, int indentationLevel, int lineWrapLength) const; + void writeElementAsText (OutputStream&, int, int, const char*) const; void getChildElementsAsArray (XmlElement**) const noexcept; void reorderChildElements (XmlElement**, int) noexcept; XmlAttributeNode* getAttribute (StringRef) const noexcept; diff --git a/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp b/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp index b46af22..8551f2c 100644 --- a/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp @@ -154,12 +154,16 @@ bool GZIPCompressorOutputStream::setPosition (int64 /*newPosition*/) return false; } + +//============================================================================== //============================================================================== #if JUCE_UNIT_TESTS struct GZIPTests : public UnitTest { - GZIPTests() : UnitTest ("GZIP", "Compression") {} + GZIPTests() + : UnitTest ("GZIP", UnitTestCategories::compression) + {} void runTest() override { diff --git a/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp b/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp index 16290c0..624c4b1 100644 --- a/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp @@ -44,6 +44,14 @@ namespace zlibNamespace #endif #endif + #if JUCE_GCC + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wconversion" + #pragma GCC diagnostic ignored "-Wsign-conversion" + #pragma GCC diagnostic ignored "-Wshadow" + #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" + #endif + #undef OS_CODE #undef fdopen #define ZLIB_INTERNAL @@ -78,6 +86,10 @@ namespace zlibNamespace #if JUCE_CLANG #pragma clang diagnostic pop #endif + + #if JUCE_GCC + #pragma GCC diagnostic pop + #endif #else #include JUCE_ZLIB_INCLUDE_PATH @@ -298,13 +310,15 @@ bool GZIPDecompressorInputStream::setPosition (int64 newPos) return true; } + +//============================================================================== //============================================================================== #if JUCE_UNIT_TESTS struct GZIPDecompressorInputStreamTests : public UnitTest { GZIPDecompressorInputStreamTests() - : UnitTest ("GZIPDecompressorInputStreamTests", "Streams") + : UnitTest ("GZIPDecompressorInputStreamTests", UnitTestCategories::streams) {} void runTest() override diff --git a/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.cpp b/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.cpp index 6fcd0c5..4c49225 100644 --- a/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.cpp +++ b/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.cpp @@ -46,21 +46,21 @@ struct ZipFile::ZipEntryHolder entry.uncompressedSize = (int64) readUnalignedLittleEndianInt (buffer + 24); streamOffset = (int64) readUnalignedLittleEndianInt (buffer + 42); - auto externalFileAttributes = (int32) readUnalignedLittleEndianInt (buffer + 38); - auto fileType = (externalFileAttributes >> 28) & 0xf; - + entry.externalFileAttributes = readUnalignedLittleEndianInt (buffer + 38); + auto fileType = (entry.externalFileAttributes >> 28) & 0xf; entry.isSymbolicLink = (fileType == 0xA); + entry.filename = String::fromUTF8 (buffer + 46, fileNameLen); } static Time parseFileTime (uint32 time, uint32 date) noexcept { - int year = 1980 + (date >> 9); - int month = ((date >> 5) & 15) - 1; - int day = date & 31; - int hours = time >> 11; - int minutes = (time >> 5) & 63; - int seconds = (int) ((time & 31) << 1); + auto year = (int) (1980 + (date >> 9)); + auto month = (int) (((date >> 5) & 15) - 1); + auto day = (int) (date & 31); + auto hours = (int) time >> 11; + auto minutes = (int) ((time >> 5) & 63); + auto seconds = (int) ((time & 31) << 1); return { year, month, day, hours, minutes, seconds }; } @@ -369,16 +369,16 @@ void ZipFile::init() break; auto* buffer = static_cast (headerData.getData()) + pos; - auto fileNameLen = readUnalignedLittleEndianShort (buffer + 28); + auto fileNameLen = readUnalignedLittleEndianShort (buffer + 28u); if (pos + 46 + fileNameLen > size) break; entries.add (new ZipEntryHolder (buffer, fileNameLen)); - pos += 46 + fileNameLen - + readUnalignedLittleEndianShort (buffer + 30) - + readUnalignedLittleEndianShort (buffer + 32); + pos += 46u + fileNameLen + + readUnalignedLittleEndianShort (buffer + 30u) + + readUnalignedLittleEndianShort (buffer + 32u); } } } @@ -638,12 +638,16 @@ bool ZipFile::Builder::writeToStream (OutputStream& target, double* const progre return true; } + +//============================================================================== //============================================================================== #if JUCE_UNIT_TESTS struct ZIPTests : public UnitTest { - ZIPTests() : UnitTest ("ZIP") {} + ZIPTests() + : UnitTest ("ZIP", UnitTestCategories::compression) + {} void runTest() override { diff --git a/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.h b/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.h index 45173da..e3f8b76 100644 --- a/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.h +++ b/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.h @@ -82,6 +82,12 @@ public: /** True if the zip entry is a symbolic link. */ bool isSymbolicLink; + + /** Platform specific data. Depending on how the zip file was created this + may contain macOS and Linux file types, permissions and + setuid/setgid/sticky bits. + */ + uint32 externalFileAttributes; }; //============================================================================== diff --git a/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp b/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp index c9a63f0..39b85dc 100644 --- a/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp @@ -186,33 +186,20 @@ bool PropertiesFile::save() bool PropertiesFile::loadAsXml() { - XmlDocument parser (file); - std::unique_ptr doc (parser.getDocumentElement (true)); - - if (doc != nullptr && doc->hasTagName (PropertyFileConstants::fileTag)) + if (auto doc = parseXMLIfTagMatches (file, PropertyFileConstants::fileTag)) { - doc.reset (parser.getDocumentElement()); - - if (doc != nullptr) + forEachXmlChildElementWithTagName (*doc, e, PropertyFileConstants::valueTag) { - forEachXmlChildElementWithTagName (*doc, e, PropertyFileConstants::valueTag) - { - auto name = e->getStringAttribute (PropertyFileConstants::nameAttribute); + auto name = e->getStringAttribute (PropertyFileConstants::nameAttribute); - if (name.isNotEmpty()) - getAllProperties().set (name, - e->getFirstChildElement() != nullptr - ? e->getFirstChildElement()->createDocument ("", true) - : e->getStringAttribute (PropertyFileConstants::valueAttribute)); - } - - return true; + if (name.isNotEmpty()) + getAllProperties().set (name, + e->getFirstChildElement() != nullptr + ? e->getFirstChildElement()->toString (XmlElement::TextFormat().singleLine().withoutHeader()) + : e->getStringAttribute (PropertyFileConstants::valueAttribute)); } - // must be a pretty broken XML file we're trying to parse here, - // or a sign that this object needs an InterProcessLock, - // or just a failure reading the file. This last reason is why - // we don't jassertfalse here. + return true; } return false; @@ -229,8 +216,8 @@ bool PropertiesFile::saveAsXml() e->setAttribute (PropertyFileConstants::nameAttribute, props.getAllKeys() [i]); // if the value seems to contain xml, store it as such.. - if (auto* childElement = XmlDocument::parse (props.getAllValues() [i])) - e->addChildElement (childElement); + if (auto childElement = parseXML (props.getAllValues() [i])) + e->addChildElement (childElement.release()); else e->setAttribute (PropertyFileConstants::valueAttribute, props.getAllValues() [i]); } @@ -240,7 +227,7 @@ bool PropertiesFile::saveAsXml() if (pl != nullptr && ! pl->isLocked()) return false; // locking failure.. - if (doc.writeToFile (file, {})) + if (doc.writeTo (file, {})) { needsWriting = false; return true; diff --git a/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h b/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h index 639fd08..1def8be 100644 --- a/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h +++ b/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h @@ -24,6 +24,7 @@ ============================================================================== */ + /******************************************************************************* The block below describes the properties of this module, and is read by the Projucer to automatically generate project code that uses it. @@ -33,15 +34,15 @@ BEGIN_JUCE_MODULE_DECLARATION - ID: juce_data_structures - vendor: juce - version: 5.4.3 - name: JUCE data model helper classes - description: Classes for undo/redo management, and smart data structures. - website: http://www.juce.com/juce - license: GPL/Commercial + ID: juce_data_structures + vendor: juce + version: 5.4.7 + name: JUCE data model helper classes + description: Classes for undo/redo management, and smart data structures. + website: http://www.juce.com/juce + license: GPL/Commercial - dependencies: juce_events + dependencies: juce_events END_JUCE_MODULE_DECLARATION diff --git a/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.cpp b/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.cpp index 64b546b..d44b1b7 100644 --- a/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.cpp @@ -146,7 +146,7 @@ bool UndoManager::perform (UndoableAction* newAction) } totalUnitsStored += action->getSizeInUnits(); - actionSet->actions.add (action.release()); + actionSet->actions.add (std::move (action)); newTransaction = false; moveFutureTransactionsToStash(); diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_CachedValue.cpp b/JuceLibraryCode/modules/juce_data_structures/values/juce_CachedValue.cpp index ac43492..dbf070b 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_CachedValue.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_CachedValue.cpp @@ -32,7 +32,9 @@ namespace juce class CachedValueTests : public UnitTest { public: - CachedValueTests() : UnitTest ("CachedValues", "Values") {} + CachedValueTests() + : UnitTest ("CachedValues", UnitTestCategories::values) + {} void runTest() override { diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_CachedValue.h b/JuceLibraryCode/modules/juce_data_structures/values/juce_CachedValue.h index 3d6a2af..6236e50 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_CachedValue.h +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_CachedValue.h @@ -197,10 +197,6 @@ private: Type getTypedValue() const; void valueTreePropertyChanged (ValueTree& changedTree, const Identifier& changedProperty) override; - void valueTreeChildAdded (ValueTree&, ValueTree&) override {} - void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override {} - void valueTreeChildOrderChanged (ValueTree&, int, int) override {} - void valueTreeParentChanged (ValueTree&) override {} //============================================================================== JUCE_DECLARE_WEAK_REFERENCEABLE (CachedValue) diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.cpp b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.cpp index 91c9eb5..e185d8d 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.cpp @@ -992,9 +992,9 @@ void ValueTree::sendPropertyChangeMessage (const Identifier& property) } //============================================================================== -XmlElement* ValueTree::createXml() const +std::unique_ptr ValueTree::createXml() const { - return object != nullptr ? object->createXml() : nullptr; + return std::unique_ptr (object != nullptr ? object->createXml() : nullptr); } ValueTree ValueTree::fromXml (const XmlElement& xml) @@ -1015,12 +1015,18 @@ ValueTree ValueTree::fromXml (const XmlElement& xml) return {}; } -String ValueTree::toXmlString() const +ValueTree ValueTree::fromXml (const String& xmlText) { - std::unique_ptr xml (createXml()); + if (auto xml = parseXML (xmlText)) + return fromXml (*xml); - if (xml != nullptr) - return xml->createDocument ({}); + return {}; +} + +String ValueTree::toXmlString (const XmlElement::TextFormat& format) const +{ + if (auto xml = createXml()) + return xml->toString (format); return {}; } @@ -1088,15 +1094,24 @@ ValueTree ValueTree::readFromGZIPData (const void* data, size_t numBytes) return readFromStream (gzipStream); } -void ValueTree::Listener::valueTreeRedirected (ValueTree&) {} +void ValueTree::Listener::valueTreePropertyChanged (ValueTree&, const Identifier&) {} +void ValueTree::Listener::valueTreeChildAdded (ValueTree&, ValueTree&) {} +void ValueTree::Listener::valueTreeChildRemoved (ValueTree&, ValueTree&, int) {} +void ValueTree::Listener::valueTreeChildOrderChanged (ValueTree&, int, int) {} +void ValueTree::Listener::valueTreeParentChanged (ValueTree&) {} +void ValueTree::Listener::valueTreeRedirected (ValueTree&) {} + +//============================================================================== //============================================================================== #if JUCE_UNIT_TESTS class ValueTreeTests : public UnitTest { public: - ValueTreeTests() : UnitTest ("ValueTrees", "Values") {} + ValueTreeTests() + : UnitTest ("ValueTrees", UnitTestCategories::values) + {} static String createRandomIdentifier (Random& r) { @@ -1179,8 +1194,8 @@ public: } expect (v1.isEquivalentTo (ValueTree::readFromGZIPData (zipped.getData(), zipped.getDataSize()))); - std::unique_ptr xml1 (v1.createXml()); - std::unique_ptr xml2 (v2.createCopy().createXml()); + auto xml1 = v1.createXml(); + auto xml2 = v2.createCopy().createXml(); expect (xml1->isEquivalentTo (xml2.get(), false)); auto v4 = v2.createCopy(); diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.h b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.h index d9c0443..0f4f725 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.h +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.h @@ -62,7 +62,7 @@ namespace juce will correspond to the order in which the property was added, or that it will remain constant when other properties are added or removed. - Listeners can be added to a ValueTree to be told when properies change and when + Listeners can be added to a ValueTree to be told when properties change and when sub-trees are added or removed. @see var, XmlElement @@ -430,7 +430,7 @@ public: The caller must delete the object that is returned. @see fromXml, toXmlString */ - XmlElement* createXml() const; + std::unique_ptr createXml() const; /** Tries to recreate a tree from its XML representation. This isn't designed to cope with random XML data - it should only be fed XML that was created @@ -438,11 +438,17 @@ public: */ static ValueTree fromXml (const XmlElement& xml); + /** Tries to recreate a tree from its XML representation. + This isn't designed to cope with random XML data - it should only be fed XML that was created + by the createXml() method. + */ + static ValueTree fromXml (const String& xmlText); + /** This returns a string containing an XML representation of the tree. This is quite handy for debugging purposes, as it provides a quick way to view a tree. @see createXml() */ - String toXmlString() const; + String toXmlString (const XmlElement::TextFormat& format = {}) const; //============================================================================== /** Stores this tree (and all its children) in a binary format. @@ -484,7 +490,7 @@ public: simply check the tree parameter in this callback to make sure it's the tree you're interested in. */ virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, - const Identifier& property) = 0; + const Identifier& property); /** This method is called when a child sub-tree is added. Note that when you register a listener to a tree, it will receive this callback for @@ -493,7 +499,7 @@ public: just check the parentTree parameter to make sure it's the one that you're interested in. */ virtual void valueTreeChildAdded (ValueTree& parentTree, - ValueTree& childWhichHasBeenAdded) = 0; + ValueTree& childWhichHasBeenAdded); /** This method is called when a child sub-tree is removed. @@ -504,7 +510,7 @@ public: */ virtual void valueTreeChildRemoved (ValueTree& parentTree, ValueTree& childWhichHasBeenRemoved, - int indexFromWhichChildWasRemoved) = 0; + int indexFromWhichChildWasRemoved); /** This method is called when a tree's children have been re-shuffled. @@ -514,7 +520,7 @@ public: just check the parameter to make sure it's the tree that you're interested in. */ virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved, - int oldIndex, int newIndex) = 0; + int oldIndex, int newIndex); /** This method is called when a tree has been added or removed from a parent. @@ -522,7 +528,7 @@ public: removed from a parent. Unlike the other callbacks, it applies only to the tree to which the listener is registered, and not to any of its children. */ - virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) = 0; + virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged); /** This method is called when a tree is made to point to a different internal shared object. When operator= is used to make a ValueTree refer to a different object, this callback diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.cpp b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.cpp index 7e49e71..68cca26 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.cpp @@ -159,8 +159,6 @@ void ValueTreeSynchroniser::valueTreeChildOrderChanged (ValueTree& parent, int o stateChanged (m.getData(), m.getDataSize()); } -void ValueTreeSynchroniser::valueTreeParentChanged (ValueTree&) {} // (No action needed here) - bool ValueTreeSynchroniser::applyChange (ValueTree& root, const void* data, size_t dataSize, UndoManager* undoManager) { MemoryInputStream input (data, dataSize, false); diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.h b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.h index 61e73da..5d5a983 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.h +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.h @@ -91,7 +91,6 @@ private: void valueTreeChildAdded (ValueTree&, ValueTree&) override; void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override; void valueTreeChildOrderChanged (ValueTree&, int, int) override; - void valueTreeParentChanged (ValueTree&) override; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreeSynchroniser) }; diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueWithDefault.cpp b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueWithDefault.cpp index cf7414d..8cd16df 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueWithDefault.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueWithDefault.cpp @@ -32,7 +32,9 @@ namespace juce class ValueWithDefaultTests : public UnitTest { public: - ValueWithDefaultTests() : UnitTest ("ValueWithDefault", "Values") {} + ValueWithDefaultTests() + : UnitTest ("ValueWithDefault", UnitTestCategories::values) + {} void runTest() override { diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.cpp b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.cpp index 7112a81..c900c00 100644 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.cpp +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.cpp @@ -51,7 +51,7 @@ private: //============================================================================== ActionBroadcaster::ActionBroadcaster() { - // are you trying to create this object before or after juce has been intialised?? + // are you trying to create this object before or after juce has been initialised?? JUCE_ASSERT_MESSAGE_MANAGER_EXISTS } diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.cpp b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.cpp index 0b8941c..39ad7a5 100644 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.cpp +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.cpp @@ -39,6 +39,7 @@ void ChangeBroadcaster::addChangeListener (ChangeListener* const listener) JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED changeListeners.add (listener); + anyListeners = true; } void ChangeBroadcaster::removeChangeListener (ChangeListener* const listener) @@ -48,6 +49,7 @@ void ChangeBroadcaster::removeChangeListener (ChangeListener* const listener) JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED changeListeners.remove (listener); + anyListeners = changeListeners.size() > 0; } void ChangeBroadcaster::removeAllChangeListeners() @@ -57,11 +59,12 @@ void ChangeBroadcaster::removeAllChangeListeners() JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED changeListeners.clear(); + anyListeners = false; } void ChangeBroadcaster::sendChangeMessage() { - if (changeListeners.size() > 0) + if (anyListeners) broadcastCallback.triggerAsyncUpdate(); } diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.h b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.h index 4777502..2bf5ecc 100644 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.h +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.h @@ -95,6 +95,8 @@ private: ChangeBroadcasterCallback broadcastCallback; ListenerList changeListeners; + std::atomic anyListeners { false }; + void callListeners(); JUCE_DECLARE_NON_COPYABLE (ChangeBroadcaster) diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeListener.h b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeListener.h index b34dbe4..362ff15 100644 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeListener.h +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeListener.h @@ -51,13 +51,6 @@ public: @param source the ChangeBroadcaster that triggered the callback. */ virtual void changeListenerCallback (ChangeBroadcaster* source) = 0; - - - //============================================================================== - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // This method's signature has changed to take a ChangeBroadcaster parameter - please update your code! - private: virtual int changeListenerCallback (void*) { return 0; } - #endif }; } // namespace juce diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.cpp b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.cpp index a74979a..57c5291 100644 --- a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.cpp +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.cpp @@ -290,7 +290,7 @@ bool InterprocessConnection::readNextMessage() uint32 messageHeader[2]; auto bytes = readData (messageHeader, sizeof (messageHeader)); - if (bytes == sizeof (messageHeader) + if (bytes == (int) sizeof (messageHeader) && ByteOrder::swapIfBigEndian (messageHeader[0]) == magicMessageHeader) { auto bytesInMessage = (int) ByteOrder::swapIfBigEndian (messageHeader[1]); diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.h b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.h index 628c0f3..ea84e7d 100644 --- a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.h +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.h @@ -113,7 +113,7 @@ public: to the pipe, or -1 for an infinite timeout @param mustNotExist if set to true, the method will fail if the pipe already exists @returns true if the pipe was created, or false if it fails (e.g. if another process is - already using using the pipe) + already using the pipe) */ bool createPipe (const String& pipeName, int pipeReceiveMessageTimeoutMs, bool mustNotExist = false); diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_NetworkServiceDiscovery.cpp b/JuceLibraryCode/modules/juce_events/interprocess/juce_NetworkServiceDiscovery.cpp index 1e5474f..f2ddcc5 100644 --- a/JuceLibraryCode/modules/juce_events/interprocess/juce_NetworkServiceDiscovery.cpp +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_NetworkServiceDiscovery.cpp @@ -23,6 +23,11 @@ namespace juce { +#if JUCE_ANDROID + extern void acquireMulticastLock(); + extern void releaseMulticastLock(); +#endif + NetworkServiceDiscovery::Advertiser::Advertiser (const String& serviceTypeUID, const String& serviceDescription, int broadcastPortToUse, int connectionPort, @@ -65,7 +70,7 @@ void NetworkServiceDiscovery::Advertiser::sendBroadcast() auto localAddress = IPAddress::getLocalAddress(); message.setAttribute ("address", localAddress.toString()); auto broadcastAddress = IPAddress::getInterfaceBroadcastAddress (localAddress); - auto data = message.createDocument ({}, true, false); + auto data = message.toString (XmlElement::TextFormat().singleLine().withoutHeader()); socket.write (broadcastAddress.toString(), broadcastPort, data.toRawUTF8(), (int) data.getNumBytesAsUTF8()); } @@ -73,6 +78,10 @@ void NetworkServiceDiscovery::Advertiser::sendBroadcast() NetworkServiceDiscovery::AvailableServiceList::AvailableServiceList (const String& serviceType, int broadcastPort) : Thread ("Discovery_listen"), serviceTypeUID (serviceType) { + #if JUCE_ANDROID + acquireMulticastLock(); + #endif + socket.bindToPort (broadcastPort); startThread (2); } @@ -81,6 +90,10 @@ NetworkServiceDiscovery::AvailableServiceList::~AvailableServiceList() { socket.shutdown(); stopThread (2000); + + #if JUCE_ANDROID + releaseMulticastLock(); + #endif } void NetworkServiceDiscovery::AvailableServiceList::run() diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_NetworkServiceDiscovery.h b/JuceLibraryCode/modules/juce_events/interprocess/juce_NetworkServiceDiscovery.h index 61ac68e..5c1fdcd 100644 --- a/JuceLibraryCode/modules/juce_events/interprocess/juce_NetworkServiceDiscovery.h +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_NetworkServiceDiscovery.h @@ -28,6 +28,8 @@ namespace juce Contains classes that implement a simple protocol for broadcasting the availability and location of a discoverable service on the local network, and for maintaining a list of known services. + + @tags{Events} */ struct NetworkServiceDiscovery { @@ -36,6 +38,8 @@ struct NetworkServiceDiscovery To use, simply create an instance of an Advertiser and it'll broadcast until you delete it. + + @tags{Events} */ struct Advertiser : private Thread { @@ -68,7 +72,10 @@ struct NetworkServiceDiscovery //============================================================================== /** Contains information about a service that has been found on the network. + @see AvailableServiceList, Advertiser + + @tags{Events} */ struct Service { @@ -87,7 +94,10 @@ struct NetworkServiceDiscovery Just create an instance of AvailableServiceList and it will start listening - you can register a callback with its onChange member to find out when services appear/disappear, and you can call getServices() to find out the current list. + @see Service, Advertiser + + @tags{Events} */ struct AvailableServiceList : private Thread, private AsyncUpdater @@ -103,7 +113,7 @@ struct NetworkServiceDiscovery /** Destructor */ ~AvailableServiceList() override; - /** A lambda that can be set to recieve a callback when the list changes */ + /** A lambda that can be set to receive a callback when the list changes */ std::function onChange; /** Returns a list of the currently known services. */ diff --git a/JuceLibraryCode/modules/juce_events/juce_events.h b/JuceLibraryCode/modules/juce_events/juce_events.h index ffde657..07b3ef5 100644 --- a/JuceLibraryCode/modules/juce_events/juce_events.h +++ b/JuceLibraryCode/modules/juce_events/juce_events.h @@ -20,6 +20,7 @@ ============================================================================== */ + /******************************************************************************* The block below describes the properties of this module, and is read by the Projucer to automatically generate project code that uses it. @@ -29,15 +30,15 @@ BEGIN_JUCE_MODULE_DECLARATION - ID: juce_events - vendor: juce - version: 5.4.3 - name: JUCE message and event handling classes - description: Classes for running an application's main event loop and sending/receiving messages, timers, etc. - website: http://www.juce.com/juce - license: ISC + ID: juce_events + vendor: juce + version: 5.4.7 + name: JUCE message and event handling classes + description: Classes for running an application's main event loop and sending/receiving messages, timers, etc. + website: http://www.juce.com/juce + license: ISC - dependencies: juce_core + dependencies: juce_core END_JUCE_MODULE_DECLARATION @@ -50,7 +51,7 @@ #include //============================================================================== -/** Config: JUCE_EXECUTE_APP_SUSPEND_ON_IOS_BACKGROUND_TASK +/** Config: JUCE_EXECUTE_APP_SUSPEND_ON_BACKGROUND_TASK Will execute your application's suspend method on an iOS background task, giving you extra time to save your applications state. */ diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.h b/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.h index a5f0260..1b286ca 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.h +++ b/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.h @@ -206,10 +206,17 @@ public: virtual void memoryWarningReceived() { jassertfalse; } //============================================================================== - /** Override this method to be informed when the back button is pressed on a device. + /** This will be called when the back button on a device is pressed. The return value + should be used to indicate whether the back button event has been handled by + the application, for example if you want to implement custom navigation instead + of the standard behaviour on Android. + This is currently only implemented on Android devices. + + @returns true if the event has been handled, or false if the default OS + behaviour should happen */ - virtual void backButtonPressed() {} + virtual bool backButtonPressed() { return false; } //============================================================================== /** Signals that the main message loop should stop and the application should terminate. diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.cpp b/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.cpp index 78f18aa..4a71d16 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.cpp +++ b/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.cpp @@ -34,7 +34,7 @@ void Message::messageCallback() MessageListener::MessageListener() noexcept { - // Are you trying to create a messagelistener before or after juce has been intialised?? + // Are you trying to create a messagelistener before or after juce has been initialised?? JUCE_ASSERT_MESSAGE_MANAGER_EXISTS } diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.cpp b/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.cpp index ee83d2f..726554a 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.cpp +++ b/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.cpp @@ -164,7 +164,7 @@ private: JUCE_DECLARE_NON_COPYABLE (AsyncFunctionCallback) }; -void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* const func, void* const parameter) +void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* func, void* parameter) { if (isThisTheMessageThread()) return func (parameter); @@ -184,6 +184,18 @@ void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* cons return nullptr; } +bool MessageManager::callAsync (std::function fn) +{ + struct AsyncCallInvoker : public MessageBase + { + AsyncCallInvoker (std::function f) : callback (std::move (f)) {} + void messageCallback() override { callback(); } + std::function callback; + }; + + return (new AsyncCallInvoker (std::move (fn)))->post(); +} + //============================================================================== void MessageManager::deliverBroadcastMessage (const String& value) { @@ -261,8 +273,8 @@ bool MessageManager::existsAndIsCurrentThread() noexcept struct MessageManager::Lock::BlockingMessage : public MessageManager::MessageBase { BlockingMessage (const MessageManager::Lock* parent) noexcept - // need a const_cast here as VS2013 doesn't like a const pointer to be in an atomic - : owner (const_cast (parent)) {} + : owner (parent) + {} void messageCallback() override { @@ -277,7 +289,7 @@ struct MessageManager::Lock::BlockingMessage : public MessageManager::MessageB } CriticalSection ownerCriticalSection; - Atomic owner; + Atomic owner; WaitableEvent releaseEvent; JUCE_DECLARE_NON_COPYABLE (BlockingMessage) diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.h b/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.h index 4e57055..22b2d18 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.h +++ b/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.h @@ -94,12 +94,12 @@ public: #endif //============================================================================== - /** Asynchronously invokes a function or C++11 lambda on the message thread. */ - template - static void callAsync (FunctionType functionToCall) - { - new AsyncCallInvoker (functionToCall); - } + /** Asynchronously invokes a function or C++11 lambda on the message thread. + + @returns true if the message was successfully posted to the message queue, + or false otherwise. + */ + static bool callAsync (std::function functionToCall); /** Calls a function using the message-thread. @@ -201,7 +201,7 @@ public: /** A lock you can use to lock the message manager. You can use this class with the RAII-based ScopedLock classes. */ - class Lock + class JUCE_API Lock { public: /** @@ -223,14 +223,14 @@ public: If another thread is currently using the MessageManager, this will wait until that thread releases the lock to the MessageManager. - This call will only exit if the lock was accquired by this thread. Calling abort while + This call will only exit if the lock was acquired by this thread. Calling abort while a thread is waiting for enter to finish, will have no effect. @see exit, abort */ void enter() const noexcept; - /** Attempts to lock the meesage manager and exits if abort is called. + /** Attempts to lock the message manager and exits if abort is called. This method behaves identically to enter, except that it will abort waiting for the lock if the abort method is called. @@ -270,7 +270,7 @@ public: messageManagerLock.abort(); } - @returns false if waiting for a lock was aborted, true if the lock was accquired. + @returns false if waiting for a lock was aborted, true if the lock was acquired. @see enter, abort, ScopedTryLock */ bool tryEnter() const noexcept; @@ -283,7 +283,7 @@ public: /** Unblocks a thread which is waiting in tryEnter Call this method if you want to unblock a thread which is waiting for the MessageManager lock in tryEnter. - This method does not have any effetc on a thread waiting for a lock in enter. + This method does not have any effect on a thread waiting for a lock in enter. @see tryEnter */ void abort() const noexcept; @@ -340,16 +340,6 @@ private: static void doPlatformSpecificShutdown(); static bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages); - template - struct AsyncCallInvoker : public MessageBase - { - AsyncCallInvoker (FunctionType f) : callback (f) { post(); } - void messageCallback() override { callback(); } - FunctionType callback; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AsyncCallInvoker) - }; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MessageManager) }; diff --git a/JuceLibraryCode/modules/juce_events/native/juce_android_Messaging.cpp b/JuceLibraryCode/modules/juce_events/native/juce_android_Messaging.cpp index 7cee9b0..f563d99 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_android_Messaging.cpp +++ b/JuceLibraryCode/modules/juce_events/native/juce_android_Messaging.cpp @@ -76,7 +76,7 @@ struct AndroidMessageQueue : private Android::Runnable { } - ~AndroidMessageQueue() + ~AndroidMessageQueue() override { JUCE_ASSERT_MESSAGE_THREAD clearSingletonInstance(); @@ -156,14 +156,14 @@ void MessageManager::stopDispatchLoop() { jmethodID quitMethod = env->GetMethodID (AndroidActivity, "finishAndRemoveTask", "()V"); - if (quitMethod != 0) + if (quitMethod != nullptr) { env->CallVoidMethod (activity.get(), quitMethod); return; } quitMethod = env->GetMethodID (AndroidActivity, "finish", "()V"); - jassert (quitMethod != 0); + jassert (quitMethod != nullptr); env->CallVoidMethod (activity.get(), quitMethod); } else @@ -195,7 +195,7 @@ public: } } - ~JuceAppLifecycle() + ~JuceAppLifecycle() override { LocalRef appContext (getAppContext()); diff --git a/JuceLibraryCode/modules/juce_events/native/juce_linux_EventLoop.h b/JuceLibraryCode/modules/juce_events/native/juce_linux_EventLoop.h index ce99bd7..4aac096 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_linux_EventLoop.h +++ b/JuceLibraryCode/modules/juce_events/native/juce_linux_EventLoop.h @@ -25,31 +25,26 @@ namespace juce namespace LinuxEventLoop { - struct CallbackFunctionBase - { - virtual ~CallbackFunctionBase() {} - virtual bool operator()(int fd) = 0; - bool active = true; - }; + /** Registers a callback that will be called when a file descriptor is ready for I/O. - template - struct CallbackFunction : public CallbackFunctionBase - { - FdCallbackFunction callback; + This will add the given file descriptor to the internal set of file descriptors + that will be passed to the poll() call. When this file descriptor has data to read + the readCallback will be called. - CallbackFunction (FdCallbackFunction c) : callback (c) {} + @param fd the file descriptor to be monitored + @param readCallback a callback that will be called when the file descriptor has + data to read. The file descriptor will be passed as an argument + @param eventMask a bit mask specifying the events you are interested in for the + file descriptor. The possible values for this are defined in + + */ + void registerFdCallback (int fd, std::function readCallback, short eventMask = 1 /*POLLIN*/); - bool operator() (int fd) override { return callback (fd); } - }; + /** Unregisters a previously registered file descriptor. - template - void setWindowSystemFd (int fd, FdCallbackFunction readCallback) - { - setWindowSystemFdInternal (fd, new CallbackFunction (readCallback)); - } - void removeWindowSystemFd() noexcept; - - void setWindowSystemFdInternal (int fd, CallbackFunctionBase* readCallback) noexcept; + @see registerFdCallback + */ + void unregisterFdCallback (int fd); } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_events/native/juce_linux_Messaging.cpp b/JuceLibraryCode/modules/juce_events/native/juce_linux_Messaging.cpp index bcb3962..49b32b9 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_linux_Messaging.cpp +++ b/JuceLibraryCode/modules/juce_events/native/juce_linux_Messaging.cpp @@ -20,15 +20,6 @@ ============================================================================== */ -#include - -enum FdType -{ - INTERNAL_QUEUE_FD, - WINDOW_SYSTEM_FD, - FD_COUNT, -}; - namespace juce { @@ -38,30 +29,28 @@ class InternalMessageQueue public: InternalMessageQueue() { - auto ret = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, fd); - ignoreUnused (ret); jassert (ret == 0); + auto err = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, msgpipe); + jassert (err == 0); + ignoreUnused (err); - auto internalQueueCb = [this] (int _fd) - { - if (const MessageManager::MessageBase::Ptr msg = this->popNextMessage (_fd)) - { - JUCE_TRY - { - msg->messageCallback(); - return true; - } - JUCE_CATCH_EXCEPTION - } - return false; - }; - - pfds[INTERNAL_QUEUE_FD].fd = getReadHandle(); - pfds[INTERNAL_QUEUE_FD].events = POLLIN; - readCallback[INTERNAL_QUEUE_FD].reset (new LinuxEventLoop::CallbackFunction (internalQueueCb)); + LinuxEventLoop::registerFdCallback (getReadHandle(), + [this] (int fd) + { + while (auto msg = popNextMessage (fd)) + { + JUCE_TRY + { + msg->messageCallback(); + } + JUCE_CATCH_EXCEPTION + } + }); } ~InternalMessageQueue() { + LinuxEventLoop::unregisterFdCallback (getReadHandle()); + close (getReadHandle()); close (getWriteHandle()); @@ -74,80 +63,32 @@ public: ScopedLock sl (lock); queue.add (msg); - const int maxBytesInSocketQueue = 128; - if (bytesInSocket < maxBytesInSocketQueue) { bytesInSocket++; ScopedUnlock ul (lock); - const unsigned char x = 0xff; - ssize_t bytesWritten = write (getWriteHandle(), &x, 1); - ignoreUnused (bytesWritten); + unsigned char x = 0xff; + auto numBytes = write (getWriteHandle(), &x, 1); + ignoreUnused (numBytes); } } - void setWindowSystemFd (int _fd, LinuxEventLoop::CallbackFunctionBase* _readCallback) - { - jassert (fdCount == 1); - - ScopedLock sl (lock); - - fdCount = 2; - pfds[WINDOW_SYSTEM_FD].fd = _fd; - pfds[WINDOW_SYSTEM_FD].events = POLLIN; - readCallback[WINDOW_SYSTEM_FD].reset (_readCallback); - readCallback[WINDOW_SYSTEM_FD]->active = true; - } - - void removeWindowSystemFd() - { - jassert (fdCount == FD_COUNT); - - ScopedLock sl (lock); - - fdCount = 1; - readCallback[WINDOW_SYSTEM_FD]->active = false; - } - - bool dispatchNextEvent() noexcept - { - for (int counter = 0; counter < fdCount; counter++) - { - const int i = loopCount++; - loopCount %= fdCount; - - if (readCallback[i] != nullptr && readCallback[i]->active) - if ((*readCallback[i]) (pfds[i].fd)) - return true; - } - - return false; - } - - bool sleepUntilEvent (const int timeoutMs) - { - const int pnum = poll (pfds, static_cast (fdCount), timeoutMs); - return (pnum > 0); - } - //============================================================================== - JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (InternalMessageQueue) + JUCE_DECLARE_SINGLETON (InternalMessageQueue, false) private: CriticalSection lock; ReferenceCountedArray queue; - int fd[2]; - pollfd pfds[FD_COUNT]; - std::unique_ptr readCallback[FD_COUNT]; - int fdCount = 1; - int loopCount = 0; + + int msgpipe[2]; int bytesInSocket = 0; + static constexpr int maxBytesInSocketQueue = 128; - int getWriteHandle() const noexcept { return fd[0]; } - int getReadHandle() const noexcept { return fd[1]; } + int getWriteHandle() const noexcept { return msgpipe[0]; } + int getReadHandle() const noexcept { return msgpipe[1]; } - MessageManager::MessageBase::Ptr popNextMessage (int _fd) noexcept + MessageManager::MessageBase::Ptr popNextMessage (int fd) noexcept { const ScopedLock sl (lock); @@ -155,9 +96,9 @@ private: { --bytesInSocket; - const ScopedUnlock ul (lock); + ScopedUnlock ul (lock); unsigned char x; - ssize_t numBytes = read (_fd, &x, 1); + auto numBytes = read (fd, &x, 1); ignoreUnused (numBytes); } @@ -167,13 +108,95 @@ private: JUCE_IMPLEMENT_SINGLETON (InternalMessageQueue) +//============================================================================== +struct InternalRunLoop +{ +public: + InternalRunLoop() + { + fdReadCallbacks.reserve (8); + } + + void registerFdCallback (int fd, std::function&& cb, short eventMask) + { + const ScopedLock sl (lock); + + fdReadCallbacks.push_back ({ fd, std::move (cb) }); + pfds.push_back ({ fd, eventMask, 0 }); + } + + void unregisterFdCallback (int fd) + { + const ScopedLock sl (lock); + + { + auto removePredicate = [=] (const std::pair>& cb) { return cb.first == fd; }; + + fdReadCallbacks.erase (std::remove_if (std::begin (fdReadCallbacks), std::end (fdReadCallbacks), removePredicate), + std::end (fdReadCallbacks)); + } + + { + auto removePredicate = [=] (const pollfd& pfd) { return pfd.fd == fd; }; + + pfds.erase (std::remove_if (std::begin (pfds), std::end (pfds), removePredicate), + std::end (pfds)); + } + } + + bool dispatchPendingEvents() + { + const ScopedLock sl (lock); + + if (poll (&pfds.front(), static_cast (pfds.size()), 0) == 0) + return false; + + bool eventWasSent = false; + + for (auto& pfd : pfds) + { + if (pfd.revents == 0) + continue; + + pfd.revents = 0; + + auto fd = pfd.fd; + + for (auto& fdAndCallback : fdReadCallbacks) + { + if (fdAndCallback.first == fd) + { + fdAndCallback.second (fd); + eventWasSent = true; + } + } + } + + return eventWasSent; + } + + void sleepUntilNextEvent (int timeoutMs) + { + poll (&pfds.front(), static_cast (pfds.size()), timeoutMs); + } + + //============================================================================== + JUCE_DECLARE_SINGLETON (InternalRunLoop, false) + +private: + CriticalSection lock; + + std::vector>> fdReadCallbacks; + std::vector pfds; +}; + +JUCE_IMPLEMENT_SINGLETON (InternalRunLoop) //============================================================================== namespace LinuxErrorHandling { static bool keyboardBreakOccurred = false; - //============================================================================== void keyboardBreakSignalHandler (int sig) { if (sig == SIGINT) @@ -188,7 +211,7 @@ namespace LinuxErrorHandling saction.sa_handler = keyboardBreakSignalHandler; saction.sa_mask = maskSet; saction.sa_flags = 0; - sigaction (SIGINT, &saction, 0); + sigaction (SIGINT, &saction, nullptr); } } @@ -198,14 +221,14 @@ void MessageManager::doPlatformSpecificInitialisation() if (JUCEApplicationBase::isStandaloneApp()) LinuxErrorHandling::installKeyboardBreakHandler(); - // Create the internal message queue - auto* queue = InternalMessageQueue::getInstance(); - ignoreUnused (queue); + InternalRunLoop::getInstance(); + InternalMessageQueue::getInstance(); } void MessageManager::doPlatformSpecificShutdown() { InternalMessageQueue::deleteInstance(); + InternalRunLoop::deleteInstance(); } bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message) @@ -230,18 +253,17 @@ bool MessageManager::dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMes for (;;) { if (LinuxErrorHandling::keyboardBreakOccurred) - JUCEApplicationBase::getInstance()->quit(); + JUCEApplicationBase::quit(); - if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating()) + if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating()) { - if (queue->dispatchNextEvent()) + if (runLoop->dispatchPendingEvents()) break; if (returnIfNoPendingMessages) return false; - // wait for 2000ms for next events if necessary - queue->sleepUntilEvent (2000); + runLoop->sleepUntilNextEvent (2000); } } @@ -249,17 +271,16 @@ bool MessageManager::dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMes } //============================================================================== -void LinuxEventLoop::setWindowSystemFdInternal (int fd, LinuxEventLoop::CallbackFunctionBase* readCallback) noexcept +void LinuxEventLoop::registerFdCallback (int fd, std::function readCallback, short eventMask) { - if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating()) - queue->setWindowSystemFd (fd, readCallback); + if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating()) + runLoop->registerFdCallback (fd, std::move (readCallback), eventMask); } -void LinuxEventLoop::removeWindowSystemFd() noexcept +void LinuxEventLoop::unregisterFdCallback (int fd) { - if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating()) - queue->removeWindowSystemFd(); + if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating()) + runLoop->unregisterFdCallback (fd); } - } // namespace juce diff --git a/JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm b/JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm index ddc09cf..36359d9 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm +++ b/JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm @@ -389,18 +389,19 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) { JUCE_AUTORELEASEPOOL { - CFRunLoopRunInMode (kCFRunLoopDefaultMode, 0.001, true); + auto msRemaining = endTime - Time::currentTimeMillis(); - NSEvent* e = [NSApp nextEventMatchingMask: NSEventMaskAny - untilDate: [NSDate dateWithTimeIntervalSinceNow: 0.001] - inMode: NSDefaultRunLoopMode - dequeue: YES]; - - if (e != nil && (isEventBlockedByModalComps == nullptr || ! (*isEventBlockedByModalComps) (e))) - [NSApp sendEvent: e]; - - if (Time::currentTimeMillis() >= endTime) + if (msRemaining <= 0) break; + + CFRunLoopRunInMode (kCFRunLoopDefaultMode, jmin (1.0, msRemaining * 0.001), true); + + if (NSEvent* e = [NSApp nextEventMatchingMask: NSEventMaskAny + untilDate: [NSDate dateWithTimeIntervalSinceNow: 0.001] + inMode: NSDefaultRunLoopMode + dequeue: YES]) + if (isEventBlockedByModalComps == nullptr || ! (*isEventBlockedByModalComps) (e)) + [NSApp sendEvent: e]; } } diff --git a/JuceLibraryCode/modules/juce_events/native/juce_win32_Messaging.cpp b/JuceLibraryCode/modules/juce_events/native/juce_win32_Messaging.cpp index d9eabdf..e0f8945 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_win32_Messaging.cpp +++ b/JuceLibraryCode/modules/juce_events/native/juce_win32_Messaging.cpp @@ -25,40 +25,159 @@ namespace juce extern HWND juce_messageWindowHandle; +#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client && JucePlugin_Build_Unity + bool juce_isRunningInUnity(); +#endif + +#if JUCE_MODULE_AVAILABLE_juce_gui_extra + LRESULT juce_offerEventToActiveXControl (::MSG&); +#endif + using CheckEventBlockedByModalComps = bool (*)(const MSG&); CheckEventBlockedByModalComps isEventBlockedByModalComps = nullptr; using SettingChangeCallbackFunc = void (*)(void); SettingChangeCallbackFunc settingChangeCallback = nullptr; -#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client && JucePlugin_Build_Unity - bool juce_isRunningInUnity(); -#endif - //============================================================================== -namespace WindowsMessageHelpers +class InternalMessageQueue { - const unsigned int customMessageID = WM_USER + 123; - const unsigned int broadcastMessageMagicNumber = 0xc403; - - const TCHAR messageWindowName[] = _T("JUCEWindow"); - std::unique_ptr messageWindow; - - void dispatchMessageFromLParam (LPARAM lParam) +public: + InternalMessageQueue() { - if (auto message = reinterpret_cast (lParam)) - { - JUCE_TRY - { - message->messageCallback(); - } - JUCE_CATCH_EXCEPTION + messageWindow = std::make_unique (messageWindowName, (WNDPROC) messageWndProc); + juce_messageWindowHandle = messageWindow->getHWND(); + } - message->decReferenceCount(); + ~InternalMessageQueue() + { + juce_messageWindowHandle = 0; + clearSingletonInstance(); + } + + JUCE_DECLARE_SINGLETON (InternalMessageQueue, false) + + //============================================================================== + void broadcastMessage (const String& message) + { + auto localCopy = message; + + Array windows; + EnumWindows (&broadcastEnumWindowProc, (LPARAM) &windows); + + for (int i = windows.size(); --i >= 0;) + { + COPYDATASTRUCT data; + data.dwData = broadcastMessageMagicNumber; + data.cbData = (localCopy.length() + 1) * sizeof (CharPointer_UTF32::CharType); + data.lpData = (void*) localCopy.toUTF32().getAddress(); + + DWORD_PTR result; + SendMessageTimeout (windows.getUnchecked (i), WM_COPYDATA, + (WPARAM) juce_messageWindowHandle, + (LPARAM) &data, + SMTO_BLOCK | SMTO_ABORTIFHUNG, 8000, &result); } } - BOOL CALLBACK broadcastEnumWindowProc (HWND hwnd, LPARAM lParam) + void postMessage (MessageManager::MessageBase* message) + { + bool shouldTriggerMessageQueueDispatch = false; + + { + const ScopedLock sl (lock); + + shouldTriggerMessageQueueDispatch = messageQueue.isEmpty(); + messageQueue.add (message); + } + + if (! shouldTriggerMessageQueueDispatch) + return; + + #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client && JucePlugin_Build_Unity + if (juce_isRunningInUnity()) + { + SendNotifyMessage (juce_messageWindowHandle, customMessageID, 0, 0); + return; + } + #endif + + PostMessage (juce_messageWindowHandle, customMessageID, 0, 0); + } + + bool dispatchNextMessage (bool returnIfNoPendingMessages) + { + MSG m; + + if (returnIfNoPendingMessages && ! PeekMessage (&m, (HWND) 0, 0, 0, PM_NOREMOVE)) + return false; + + if (GetMessage (&m, (HWND) 0, 0, 0) >= 0) + { + #if JUCE_MODULE_AVAILABLE_juce_gui_extra + if (juce_offerEventToActiveXControl (m) != S_FALSE) + return true; + #endif + + if (m.message == customMessageID && m.hwnd == juce_messageWindowHandle) + { + dispatchMessages(); + } + else if (m.message == WM_QUIT) + { + if (auto* app = JUCEApplicationBase::getInstance()) + app->systemRequestedQuit(); + } + else if (isEventBlockedByModalComps == nullptr || ! isEventBlockedByModalComps (m)) + { + if ((m.message == WM_LBUTTONDOWN || m.message == WM_RBUTTONDOWN) + && ! JuceWindowIdentifier::isJUCEWindow (m.hwnd)) + { + // if it's someone else's window being clicked on, and the focus is + // currently on a juce window, pass the kb focus over.. + auto currentFocus = GetFocus(); + + if (currentFocus == 0 || JuceWindowIdentifier::isJUCEWindow (currentFocus)) + SetFocus (m.hwnd); + } + + TranslateMessage (&m); + DispatchMessage (&m); + } + } + + return true; + } + +private: + //============================================================================== + static LRESULT CALLBACK messageWndProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam) noexcept + { + if (h == juce_messageWindowHandle) + { + if (message == customMessageID) + { + if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating()) + queue->dispatchMessages(); + + return 0; + } + + if (message == WM_COPYDATA) + { + handleBroadcastMessage (reinterpret_cast (lParam)); + return 0; + } + + if (message == WM_SETTINGCHANGE) + if (settingChangeCallback != nullptr) + settingChangeCallback(); + } + + return DefWindowProc (h, message, wParam, lParam); + } + + static BOOL CALLBACK broadcastEnumWindowProc (HWND hwnd, LPARAM lParam) { if (hwnd != juce_messageWindowHandle) { @@ -72,7 +191,18 @@ namespace WindowsMessageHelpers return TRUE; } - void handleBroadcastMessage (const COPYDATASTRUCT* data) + static void dispatchMessage (MessageManager::MessageBase* message) + { + JUCE_TRY + { + message->messageCallback(); + } + JUCE_CATCH_EXCEPTION + + message->decReferenceCount(); + } + + static void handleBroadcastMessage (const COPYDATASTRUCT* data) { if (data != nullptr && data->dwData == broadcastMessageMagicNumber) { @@ -90,132 +220,81 @@ namespace WindowsMessageHelpers } } - //============================================================================== - LRESULT CALLBACK messageWndProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam) noexcept + void dispatchMessages() { - if (h == juce_messageWindowHandle) + ReferenceCountedArray messagesToDispatch; + { - if (message == customMessageID) - { - // (These are trapped early in our dispatch loop, but must also be checked - // here in case some 3rd-party code is running the dispatch loop). - dispatchMessageFromLParam (lParam); - return 0; - } + const ScopedLock sl (lock); - if (message == WM_COPYDATA) - { - handleBroadcastMessage (reinterpret_cast (lParam)); - return 0; - } + if (messageQueue.isEmpty()) + return; - if (message == WM_SETTINGCHANGE) - if (settingChangeCallback != nullptr) - settingChangeCallback(); + messagesToDispatch.swapWith (messageQueue); } - return DefWindowProc (h, message, wParam, lParam); + for (int i = 0; i < messagesToDispatch.size(); ++i) + { + auto message = messagesToDispatch.getUnchecked (i); + message->incReferenceCount(); + dispatchMessage (message.get()); + } } -} -#if JUCE_MODULE_AVAILABLE_juce_gui_extra -LRESULT juce_offerEventToActiveXControl (::MSG&); -#endif + //============================================================================== + static constexpr unsigned int customMessageID = WM_USER + 123; + static constexpr unsigned int broadcastMessageMagicNumber = 0xc403; + static const TCHAR messageWindowName[]; + + std::unique_ptr messageWindow; + + CriticalSection lock; + ReferenceCountedArray messageQueue; + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InternalMessageQueue) +}; + +JUCE_IMPLEMENT_SINGLETON (InternalMessageQueue) + +const TCHAR InternalMessageQueue::messageWindowName[] = _T("JUCEWindow"); //============================================================================== bool MessageManager::dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) { - using namespace WindowsMessageHelpers; - MSG m; + if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating()) + return queue->dispatchNextMessage (returnIfNoPendingMessages); - if (returnIfNoPendingMessages && ! PeekMessage (&m, (HWND) 0, 0, 0, PM_NOREMOVE)) - return false; - - if (GetMessage (&m, (HWND) 0, 0, 0) >= 0) - { - #if JUCE_MODULE_AVAILABLE_juce_gui_extra - if (juce_offerEventToActiveXControl (m) != S_FALSE) - return true; - #endif - - if (m.message == customMessageID && m.hwnd == juce_messageWindowHandle) - { - dispatchMessageFromLParam (m.lParam); - } - else if (m.message == WM_QUIT) - { - if (auto* app = JUCEApplicationBase::getInstance()) - app->systemRequestedQuit(); - } - else if (isEventBlockedByModalComps == nullptr || ! isEventBlockedByModalComps (m)) - { - if ((m.message == WM_LBUTTONDOWN || m.message == WM_RBUTTONDOWN) - && ! JuceWindowIdentifier::isJUCEWindow (m.hwnd)) - { - // if it's someone else's window being clicked on, and the focus is - // currently on a juce window, pass the kb focus over.. - auto currentFocus = GetFocus(); - - if (currentFocus == 0 || JuceWindowIdentifier::isJUCEWindow (currentFocus)) - SetFocus (m.hwnd); - } - - TranslateMessage (&m); - DispatchMessage (&m); - } - } - - return true; + return false; } bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message) { - message->incReferenceCount(); + if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating()) + { + queue->postMessage (message); + return true; + } - #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client && JucePlugin_Build_Unity - if (juce_isRunningInUnity()) - return SendNotifyMessage (juce_messageWindowHandle, WindowsMessageHelpers::customMessageID, 0, (LPARAM) message) != 0; - #endif - - return PostMessage (juce_messageWindowHandle, WindowsMessageHelpers::customMessageID, 0, (LPARAM) message) != 0; + return false; } void MessageManager::broadcastMessage (const String& value) { - auto localCopy = value; - - Array windows; - EnumWindows (&WindowsMessageHelpers::broadcastEnumWindowProc, (LPARAM) &windows); - - for (int i = windows.size(); --i >= 0;) - { - COPYDATASTRUCT data; - data.dwData = WindowsMessageHelpers::broadcastMessageMagicNumber; - data.cbData = (localCopy.length() + 1) * sizeof (CharPointer_UTF32::CharType); - data.lpData = (void*) localCopy.toUTF32().getAddress(); - - DWORD_PTR result; - SendMessageTimeout (windows.getUnchecked (i), WM_COPYDATA, - (WPARAM) juce_messageWindowHandle, - (LPARAM) &data, - SMTO_BLOCK | SMTO_ABORTIFHUNG, 8000, &result); - } + if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating()) + queue->broadcastMessage (value); } //============================================================================== void MessageManager::doPlatformSpecificInitialisation() { OleInitialize (0); - - using namespace WindowsMessageHelpers; - messageWindow.reset (new HiddenMessageWindow (messageWindowName, (WNDPROC) messageWndProc)); - juce_messageWindowHandle = messageWindow->getHWND(); + InternalMessageQueue::getInstance(); } void MessageManager::doPlatformSpecificShutdown() { - WindowsMessageHelpers::messageWindow = nullptr; - + InternalMessageQueue::deleteInstance(); OleUninitialize(); } diff --git a/OpenShotLibrary.jucer b/OpenShotLibrary.jucer index 6c349e2..8441a69 100644 --- a/OpenShotLibrary.jucer +++ b/OpenShotLibrary.jucer @@ -1,10 +1,10 @@ - + companyWebsite="http://www.openshot.org/" companyEmail="sales@openshot.org" + projectLineFeed=" " reportAppUsage="0" cppLanguageStandard="11"> @@ -17,27 +17,27 @@ - + - - - - - - - + + + + + + + JUCE_USE_DIRECTWRITE="0" JUCE_WEB_BROWSER="0" JUCE_INCLUDE_ZLIB_CODE="0" + JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES="1" JUCE_USE_CURL="0"/> diff --git a/include/AppConfig.h.in b/include/AppConfig.h.in index 9715e1b..60cb417 100644 --- a/include/AppConfig.h.in +++ b/include/AppConfig.h.in @@ -40,12 +40,13 @@ #endif #ifndef JUCE_REPORT_APP_USAGE - #define JUCE_REPORT_APP_USAGE 1 + #define JUCE_REPORT_APP_USAGE 0 #endif // END SECTION A #define JUCE_USE_DARK_SPLASH_SCREEN 1 +#define JUCE_PROJUCER_VERSION 0x50407 //============================================================================== #define JUCE_MODULE_AVAILABLE_juce_audio_basics 1 @@ -143,7 +144,7 @@ #endif #ifndef JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES - //#define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES 0 + #define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES 1 #endif #ifndef JUCE_INCLUDE_ZLIB_CODE @@ -159,11 +160,11 @@ #endif #ifndef JUCE_CATCH_UNHANDLED_EXCEPTIONS - //#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 1 + //#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 0 #endif #ifndef JUCE_ALLOW_STATIC_NULL_VARIABLES - //#define JUCE_ALLOW_STATIC_NULL_VARIABLES 1 + //#define JUCE_ALLOW_STATIC_NULL_VARIABLES 0 #endif #ifndef JUCE_STRICT_REFCOUNTEDPOINTER @@ -173,8 +174,8 @@ //============================================================================== // juce_events flags: -#ifndef JUCE_EXECUTE_APP_SUSPEND_ON_IOS_BACKGROUND_TASK - //#define JUCE_EXECUTE_APP_SUSPEND_ON_IOS_BACKGROUND_TASK 0 +#ifndef JUCE_EXECUTE_APP_SUSPEND_ON_BACKGROUND_TASK + //#define JUCE_EXECUTE_APP_SUSPEND_ON_BACKGROUND_TASK 0 #endif //============================================================================== diff --git a/include/JuceHeader.h.in b/include/JuceHeader.h.in index 2398df0..f6bbdc4 100644 --- a/include/JuceHeader.h.in +++ b/include/JuceHeader.h.in @@ -23,6 +23,14 @@ #include #undef Point +#if defined (JUCE_PROJUCER_VERSION) && JUCE_PROJUCER_VERSION < JUCE_VERSION + /** If you've hit this error then the version of the Projucer that was used to generate this project is + older than the version of the JUCE modules being included. To fix this error, re-save your project + using the latest version of the Projucer or, if you aren't using the Projucer to manage your project, + remove the JUCE_PROJUCER_VERSION define from the AppConfig.h file. + */ + #error "This project was last saved using an outdated version of the Projucer! Re-save this project with the latest version to fix this error." +#endif #if ! DONT_SET_USING_JUCE_NAMESPACE // If your code uses a lot of JUCE classes, then this will obviously save you // a lot of typing, but can be disabled by setting DONT_SET_USING_JUCE_NAMESPACE.